PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/home/htlwork.com/www/dev/pricelow/app/code/Razorpay/
Upload File :
Current File : //home/htlwork.com/www/dev/pricelow/app/code/Razorpay/Magento.tar
Magento/0000755000000000000000000000000014364325176011161 5ustar  rootrootMagento/etc/0000755000000000000000000000000014364325176011734 5ustar  rootrootMagento/etc/di.xml0000644000000000000000000000436714364325176013064 0ustar  rootroot<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider">
        <arguments>
            <argument name="extendedConfigData" xsi:type="array">
                <item name="razorpay_key_id" xsi:type="string">payment/razorpay/key_id</item>
                <item name="razorpay_merchant_name_override" xsi:type="string">payment/razorpay/merchant_name_override</item>
            </argument>
        </arguments>
    </type>
    <type name="Magento\Framework\App\Request\CsrfValidator">
        <plugin name="csrf_validator_skip" type="Razorpay\Magento\Plugin\CsrfValidatorSkip" />
    </type>

    <virtualType name="RazorpayLogger" type="Magento\Framework\Logger\Monolog">
	    <arguments>
	        <argument name="handlers" xsi:type="array">
	            <item name="error" xsi:type="object">Razorpay\Magento\Model\LogHandler</item>
	        </argument>
	    </arguments>
	</virtualType>

	<type name="Razorpay\Magento\Controller\Payment\Callback">
	    <arguments>
	        <argument name="logger" xsi:type="object">RazorpayLogger</argument>
	    </arguments>
	</type>

	<type name="Razorpay\Magento\Controller\Payment\Order">
	    <arguments>
	        <argument name="logger" xsi:type="object">RazorpayLogger</argument>
	    </arguments>
	</type>

	<type name="Razorpay\Magento\Controller\Payment\Webhook">
	    <arguments>
	        <argument name="logger" xsi:type="object">RazorpayLogger</argument>
	    </arguments>
	</type>

	<type name="Razorpay\Magento\Controller\Payment\WebhookOrderCron">
	    <arguments>
	        <argument name="logger" xsi:type="object">RazorpayLogger</argument>
	    </arguments>
	</type>

	<type name="Razorpay\Magento\Cron\WebhookOrderCron">
	    <arguments>
	        <argument name="logger" xsi:type="object">RazorpayLogger</argument>
	    </arguments>
	</type>

	<type name="Razorpay\Magento\Observer\AfterConfigSaveObserver">
	    <arguments>
	        <argument name="logger" xsi:type="object">RazorpayLogger</argument>
	    </arguments>
	</type>
</config>
Magento/etc/module.xml0000644000000000000000000000067114364325176013747 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Razorpay_Magento" setup_version="3.7.3">
	    <sequence>
	        <module name="Magento_Sales" />
	        <module name="Magento_Payment" />
	        <module name="Magento_Config" />
	        <module name="Magento_GraphQl"/>
	    </sequence>
	</module>
</config>Magento/etc/schema.graphqls0000644000000000000000000000207614364325176014744 0ustar  rootroottype StoreConfig @doc(description: "The type contains information about a store config") {
    razorpay_key_id: String @doc(description: "Razorpay API Key ID")
    razorpay_merchant_name_override: String @doc(description: "Razorpay Merchange Name Override")
}

type Mutation {
	setRzpPaymentDetailsOnCart(input: SetRzpPaymentDetailsOnCartInput) : SetRzpPaymentDetailsOnCartOutput @resolver(class: "Razorpay\\Magento\\Model\\Resolver\\SetRzpPaymentDetailsOnCart")
	placeRazorpayOrder (cart_id: String @doc(description: "cart_id to generate Razorpay Order ID.")) : RazorpayOrder @resolver( class: "Razorpay\\Magento\\Model\\Resolver\\PlaceRazorpayOrder") @doc(description: "Place Razorpay Order with cart amount and currency to generate RZP order ID.")
}

type RazorpayOrder {
	success: Boolean!
	rzp_order_id: String
	order_quote_id: String
	amount: String
	currency: String
	message: String
}

input SetRzpPaymentDetailsOnCartInput {
	cart_id: String!
	rzp_payment_id: String!
	rzp_order_id: String!
	rzp_signature: String!
}

type SetRzpPaymentDetailsOnCartOutput {
    cart: Cart!
}

Magento/etc/adminhtml/0000755000000000000000000000000014364325176013711 5ustar  rootrootMagento/etc/adminhtml/di.xml0000644000000000000000000000077514364325176015040 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
   <type name="Magento\Framework\Notification\MessageList">
       <arguments>
           <argument name="messages" xsi:type="array">
               <item name="customMessageNotification" xsi:type="string">Razorpay\Magento\Model\System\Message\UpgradeMessageNotification</item>
           </argument>
       </arguments>
   </type>
</config>Magento/etc/adminhtml/system.xml0000644000000000000000000001735314364325176015770 0ustar  rootroot<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="payment">
            <group id="razorpay" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
                <label><![CDATA[Razorpay&nbsp;&nbsp;<i>Accept and process multiple payments.</i>]]></label>
                <comment>
                    <![CDATA[<a href="https://razorpay.com/" target="_blank">Click here to sign up for Razorpay account</a>]]>
                </comment>
                <field id="active" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <config_path>payment/razorpay/active</config_path>
                </field>
                <field id="payment_action" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Payment Action</label>
                    <source_model>Razorpay\Magento\Model\PaymentAction</source_model>
                    <config_path>payment/razorpay/payment_action</config_path>
                </field>
                <field id="title" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Title</label>
                    <config_path>payment/razorpay/title</config_path>
                </field>
                <field id="merchant_name_override" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Merchant Name</label>
                    <config_path>payment/razorpay/merchant_name_override</config_path>
                </field>
                <field id="key_id" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>API Key</label>
                    <comment>**Required (Enter test key ID for testing)**</comment>
                    <config_path>payment/razorpay/key_id</config_path>
                </field>
                <field id="key_secret" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>API Key Secret</label>
                    <comment>**Required (Enter test key secret for testing)**</comment>
                    <config_path>payment/razorpay/key_secret</config_path>
                </field>
                <field id="skip_amount_mismatch_order" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Skip Amount Mismatch Order Creation</label>
                    <comment>** Don't Create Order in case of amount mismatch with Razorpay and cart total. Otherwise Order gets created with "Payment Review" Status. **</comment>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <config_path>payment/razorpay/skip_amount_mismatch_order</config_path>
                </field>
                <field id="enable_webhook" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0">
                    <label>Webhook Enabled</label>
                    <comment>** {{base_url}} Copy the Webhook Url from below **</comment>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <frontend_model>Razorpay\Magento\Model\WebhookUrl</frontend_model>
                    <config_path>payment/razorpay/enable_webhook</config_path>
                </field>
                <field id="webhook_secret" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0">
                    <label>Webhook Secret</label>
                    <comment>This field has to match the one set in dashboard.razorpay.com/webhooks</comment>
                    <config_path>payment/razorpay/webhook_secret</config_path>
                    <validate>required-entry</validate>
                    <depends> 
                        <field id="enable_webhook">1</field>
                    </depends>
                </field>
                <field id="webhook_events" translate="label" type="multiselect" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0">
                    <label>Webhook Events</label>
                    <source_model>Razorpay\Magento\Model\WebhookEvents</source_model>
                    <comment>List of Webhook Events that needs to be subscribed.</comment>
                    <config_path>payment/razorpay/webhook_events</config_path>
                    <validate>required-entry</validate>
                    <depends> 
                        <field id="enable_webhook">1</field>
                    </depends>
                </field>
                <field id="webhook_wait_time" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0">
                    <label>Webhook Wait Time</label>
                    <comment>**Required (Set the time in seconds, that webhook wait before creating a order from the backend for missed orders. ) **</comment>
                    <config_path>payment/razorpay/webhook_wait_time</config_path>
                    <depends> 
                        <field id="enable_webhook">1</field>
                    </depends>
                </field>
                <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>New Order Status</label>
                    <source_model>Magento\Sales\Model\Config\Source\Order\Status\Processing</source_model>
                    <config_path>payment/razorpay/order_status</config_path>
                </field>
                <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Payment from Applicable Countries</label>
                    <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model>
                    <config_path>payment/razorpay/allowspecific</config_path>
                </field>
                <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Payment from Specific Countries</label>
                    <source_model>Magento\Directory\Model\Config\Source\Country</source_model>
                    <can_be_empty>1</can_be_empty>
                    <config_path>payment/razorpay/specificcountry</config_path>
                </field>
                <field id="disable_upgrade_notice" translate="label" type="select" sortOrder="52" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Disable Razorpay Upgrade Notice</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <config_path>payment/razorpay/disable_upgrade_notice</config_path>
                    <validate>required-entry</validate>
                    <comment>**Required ( It will display the upgrade notification, If Razorpay new release available. ) **</comment>
                </field>
                <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Sort Order</label>
                    <config_path>payment/razorpay/sort_order</config_path>
                </field>
            </group>
        </section>
    </system>
</config>Magento/etc/adminhtml/events.xml0000644000000000000000000000057214364325176015743 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="admin_system_config_changed_section_payment">
        <observer name="razorpay_payment_gateway_before_config_save" instance="Razorpay\Magento\Observer\AfterConfigSaveObserver"/>
    </event>
</config>
Magento/etc/config.xml0000644000000000000000000000152514364325176013726 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <payment>
            <razorpay>
                <model>Razorpay\Magento\Model\PaymentMethod</model>
                <title>Razorpay</title>
                <merchant_name_override>Magento</merchant_name_override>
                <payment_action>authorize_capture</payment_action>
                <active>0</active>
                <order_status>processing</order_status>
                <key_id>Key ID</key_id>
                <key_secret>Key Secret</key_secret>
                <webhook_wait_time>300</webhook_wait_time>
                <skip_amount_mismatch_order>1</skip_amount_mismatch_order>
            </razorpay>
        </payment>
    </default>
</config>
Magento/etc/crontab.xml0000644000000000000000000000061014364325176014103 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="razorpay">
        <job name="razorpay_webhook_order_create" instance="Razorpay\Magento\Cron\WebhookOrderCron" method="execute">
            <schedule>*/3 * * * *</schedule>
        </job>
     </group>
</config>Magento/etc/graphql/0000755000000000000000000000000014364325176013372 5ustar  rootrootMagento/etc/graphql/di.xml0000644000000000000000000000133514364325176014512 0ustar  rootroot<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider">
        <arguments>
            <argument name="extendedConfigData" xsi:type="array">
                <item name="razorpay_key_id" xsi:type="string">payment/razorpay/key_id</item>
                <item name="razorpay_merchant_name_override" xsi:type="string">payment/razorpay/merchant_name_override</item>
            </argument>
        </arguments>
    </type>
</config>
Magento/etc/csp_whitelist.xml0000644000000000000000000000170614364325176015343 0ustar  rootroot<?xml version="1.0" encoding="UTF-8"?>
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd">
    <policies>
        <policy id="img-src">
            <values>
                <value id="razorpay_cdn" type="host">cdn.razorpay.com</value>
            </values>
        </policy>
        <policy id="script-src">
            <values>
                <value id="razorpay_checkout" type="host">checkout.razorpay.com</value>
            </values>
        </policy>
        <policy id="frame-src">
            <values>
                <value id="razorpay_api" type="host">api.razorpay.com</value>
            </values>
        </policy>
        <policy id="connect-src">
            <values>
                <value id="razorpay_lumberjack" type="host">lumberjack.razorpay.com</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>
Magento/etc/payment.xml0000644000000000000000000000051414364325176014133 0ustar  rootroot<?xml version="1.0"?>
<payment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Payment:etc/payment.xsd">
    <methods>
        <method name="razorpay">
            <allow_multiple_address>0</allow_multiple_address>
        </method>
    </methods>
</payment>
Magento/etc/events.xml0000644000000000000000000000141414364325176013762 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_order_place_after">
        <observer name="razorpay_payment_gateway_after_place_order" instance="Razorpay\Magento\Observer\AfterPlaceOrderObserver"/>
    </event>

    <event name="checkout_submit_before">
        <observer name="razorpay_payment_ignore_billing_address_validation" instance="Razorpay\Magento\Observer\IgnoreBillingAddressValidation" />
    </event>

    <event name="sales_model_service_quote_submit_before">
        <observer name="razorpay_payment_ignore_billing_address_validation" instance="Razorpay\Magento\Observer\IgnoreBillingAddressValidation" />
    </event>
</config>
Magento/etc/frontend/0000755000000000000000000000000014364325176013553 5ustar  rootrootMagento/etc/frontend/di.xml0000644000000000000000000000076414364325176014700 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\Model\CompositeConfigProvider">
        <arguments>
            <argument name="configProviders" xsi:type="array">
                <item name="razorpay_config_provider" xsi:type="object">Razorpay\Magento\Model\ConfigProvider</item>
            </argument>
        </arguments>
    </type>
</config>
Magento/etc/frontend/routes.xml0000644000000000000000000000050314364325176015614 0ustar  rootroot<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="razorpay" frontName="razorpay">
            <module name="Razorpay_Magento" />
        </route>
    </router>
</config>Magento/etc/cron_groups.xml0000644000000000000000000000134214364325176015016 0ustar  rootroot<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/cron_groups.xsd">
    <group id="razorpay">
        <schedule_generate_every>1</schedule_generate_every>
        <schedule_ahead_for>4</schedule_ahead_for>
        <schedule_lifetime>2</schedule_lifetime>
        <history_cleanup_every>30</history_cleanup_every>
        <history_success_lifetime>60</history_success_lifetime>
        <history_failure_lifetime>600</history_failure_lifetime>
        <use_separate_process>1</use_separate_process>
    </group>
</config>

Magento/composer.json0000644000000000000000000000225614364325176013710 0ustar  rootroot{
    "name": "razorpay/magento",
    "description": "Razorpay Magento 2.0 plugin for accepting payments.",
    "version": "3.7.3",
    "require": {
        "php": "~5.5.0|~5.6.0|^7.0",
        "razorpay/razorpay": "2.*"
    },
    "keywords": [
        "razorpay",
        "magento",
        "payments",
        "india",
        "payment gateway",
        "wallets",
        "netbanking"
    ],
    "authors": [
        {
            "name": "Team Razorpay",
            "homepage": "https://razorpay.com/about/",
            "role": "Developer"
        }
    ],
    "support": {
        "source": "https://github.com/razorpay/razorpay-magento",
        "issues": "https://razorpay.com/support/",
        "rss": "https://github.com/razorpay/razorpay-magento/releases.atom",
        "docs":
            "https://docs.razorpay.com/v1/page/integrating-your-magento-store-with-razorpay"
    },
    "homepage": "https://razorpay.com",
    "suggest": {
        "magento/module-checkout-agreements": "100.0.*"
    },
    "type": "magento2-module",
    "license": ["MIT"],
    "autoload": {
        "files": ["registration.php"],
        "psr-4": {
            "Razorpay\\Magento\\": ""
        }
    }
}
Magento/Observer/0000755000000000000000000000000014364325176012750 5ustar  rootrootMagento/Observer/IgnoreBillingAddressValidation.php0000644000000000000000000000136214364325176021530 0ustar  rootroot<?php

namespace Razorpay\Magento\Observer;

use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

/**
 * Class IgnoreBillingAddressValidation
 * @package Razorpay\Magento\Observer
 */
class IgnoreBillingAddressValidation implements ObserverInterface
{
    public function execute(Observer $observer)
    {    	
        $quote = $observer->getEvent()->getQuote();
        
        if (PaymentMethod::METHOD_CODE === $quote->getPayment()->getMethod())
        {
            $quote->getBillingAddress()->setShouldIgnoreValidation(true);
            $quote->getShippingAddress()->setShouldIgnoreValidation(true);
        }
    }
}
Magento/Observer/AfterPlaceOrderObserver.php0000644000000000000000000001102414364325176020171 0ustar  rootroot<?php

namespace Razorpay\Magento\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Payment;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\Exception\LocalizedException;

/**
 * Class AfterPlaceOrderObserver
 * @package Razorpay\Magento\Observer
 */
class AfterPlaceOrderObserver implements ObserverInterface
{

    /**
     * Store key
     */
    const STORE = 'store';

    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;



    /**
     * StatusAssignObserver constructor.
     *
     * @param OrderRepositoryInterface $orderRepository
     */
    public function __construct(
        OrderRepositoryInterface $orderRepository,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Razorpay\Magento\Model\Config $config,
        \Razorpay\Magento\Model\PaymentMethod $rzpMethod
    ) {
        $this->orderRepository = $orderRepository;
        $this->checkoutSession = $checkoutSession;
        $this->config = $config;

        $this->rzpMethod = $rzpMethod;
    }

    /**
     * {@inheritdoc}
     */
    public function execute(Observer $observer)
    { 

        $order = $observer->getOrder();

        /** @var Payment $payment */
        $payment = $order->getPayment();

        $pay_method = $payment->getMethodInstance();

        $code = $pay_method->getCode();

        if($code === PaymentMethod::METHOD_CODE)
        {
            $this->updateOrderLinkStatus($payment);
            
        }
        
    }

    /**
     * @param Payment $payment
     *
     * @return void
     */
    private function updateOrderLinkStatus(Payment $payment)
    {
        $order = $payment->getOrder();

        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();

        $lastQuoteId = $order->getQuoteId();
        $rzpPaymentId  = $payment->getLastTransId();

        if((empty($rzpPaymentId) === false) and substr($rzpPaymentId, 0,4) === 'pay_')
        {
            //get razorpay orderLink
            $orderLinkCollection = $objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                       ->getCollection()
                                                       ->addFieldToSelect('entity_id')
                                                       ->addFieldToSelect('order_placed')
                                                       ->addFieldToSelect('rzp_order_amount')
                                                       ->addFilter('quote_id', $lastQuoteId)
                                                       ->addFilter('rzp_payment_id', $rzpPaymentId)
                                                       ->addFilter('increment_order_id', $order->getRealOrderId())
                                                       ->getFirstItem();

            $orderLink = $orderLinkCollection->getData();

            if (empty($orderLink['entity_id']) === false and !$orderLink['order_placed'])
            {

                $amountPaid = number_format($this->rzpMethod->getAmountPaid($rzpPaymentId) / 100, 2, ".", "");

                //get the payment action
                $paymentAction = $this->config->getPaymentAction();

                $authOrCapture = ($paymentAction === 'authorize') ? "Authroized" : "Captured";

                $order->addStatusHistoryComment(
                            __('Actual Amount %1 of %2, with Razorpay Offer/Fee applied.', $authOrCapture, $order->getBaseCurrency()->formatTxt($amountPaid))
                        );

                //update quote
                $quote = $objectManager->get('Magento\Quote\Model\Quote')->load($lastQuoteId);

                 //validate amount before placing order
                $quoteAmount    = (int) (number_format($quote->getGrandTotal() * 100, 0, ".", ""));
                $rzpOrderAmount = (int) (number_format($orderLink['rzp_order_amount'], 0, ".", ""));

                if ($quoteAmount !== $rzpOrderAmount)
                {
                    $order->setState('processing')
                          ->setStatus('payment_review');
                }

                $order->save();

                $quote->setIsActive(false)->save();

                $this->checkoutSession->replaceQuote($quote);

                //update razorpay orderLink
                $orderLinkCollection->setOrderId($order->getEntityId())
                                    ->setOrderPlaced(true)
                                    ->save();
            }
        }
    }
}
Magento/Observer/AfterConfigSaveObserver.php0000644000000000000000000001401114364325176020174 0ustar  rootroot<?php

namespace Razorpay\Magento\Observer;

// require in case of zip installation without composer
require_once __DIR__ . "/../../Razorpay/Razorpay.php";

use Razorpay\Api\Api;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Model\Order\Payment;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\App\Config\Storage\WriterInterface;
use Magento\Framework\App\RequestInterface;
use Razorpay\Magento\Model\Config;


/**
 * Class AfterConfigSaveObserver
 * @package Razorpay\Magento\Observer
 */
class AfterConfigSaveObserver implements ObserverInterface
{

    /**
     * Store key
     */
    const STORE = 'store';


    private $request;
    private $configWriter;

    /**
     * StatusAssignObserver constructor.
     *
     * @param OrderRepositoryInterface $orderRepository
     */
    public function __construct(
        \Razorpay\Magento\Model\Config $config,
        RequestInterface $request, 
        WriterInterface $configWriter,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->config = $config;
        $this->request = $request;
        $this->configWriter = $configWriter;
        $this->_storeManager = $storeManager;
        $this->logger          = $logger;
        
        $this->config = $config;

        $this->key_id = $this->config->getConfigData(Config::KEY_PUBLIC_KEY);
        $this->key_secret = $this->config->getConfigData(Config::KEY_PRIVATE_KEY);

        $this->rzp = new Api($this->key_id, $this->key_secret);

        $this->webhookUrl = $this->_storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_WEB) . 'razorpay/payment/webhook';

        $this->webhookId = null;
    }

    /**
     * {@inheritdoc}
     */
    public function execute(Observer $observer)
    { 

        $razorpayParams = $this->request->getParam('groups')['razorpay']['fields'];
        
        $domain = parse_url($this->webhookUrl, PHP_URL_HOST);

        $domain_ip = gethostbyname($domain);

        if(isset($razorpayParams['enable_webhook']) === true)
        {
            if (!filter_var($domain_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))
            {
                $this->config->setConfigData('enable_webhook', 0);

                $this->logger->info("Can't enable/disable webhook on $domain or private ip($domain_ip).");
                return;
            }

            try
            {
                $webhookPresent = $this->getExistingWebhook();

                if(empty($razorpayParams['enable_webhook']['value']) === true)
                {
                    $this->disableWebhook();
                    return;
                }

                $events = [];

                foreach($razorpayParams['webhook_events']['value'] as $event)
                {
                    $events[$event] = true;
                }

                if(empty($this->webhookId) === false)
                {
                    $webhook = $this->rzp->webhook->edit([
                        "url" => $this->webhookUrl,
                        "events" => $events,
                        "secret" => $razorpayParams['webhook_secret']['value'],
                        "active" => true,
                    ], $this->webhookId);

                    $this->logger->info("Razorpay Webhook Updated by Admin.");
                }
                else
                {
                    $webhook = $this->rzp->webhook->create([
                        "url" => $this->webhookUrl,
                        "events" => $events,
                        "secret" => $razorpayParams['webhook_secret']['value'],
                        "active" => true,
                    ]);

                    $this->logger->info("Razorpay Webhook Created by Admin");
                }
            }
            catch(\Razorpay\Api\Errors\Error $e)
            {
                $this->logger->info($e->getMessage());
                //in case of error disable the webhook config
                $this->disableWebhook();
            }
            catch(\Exception $e)
            {
                $this->logger->info($e->getMessage());

                $this->disableWebhook();
            }
        }
        
        return;
        
    }

    /**
     * @param string $url
     *
     * @return return array
     */
    private function getExistingWebhook()
    {
        
        try
        {       
            //fetch all the webhooks 
            $webhooks = $this->rzp->webhook->all();   
            
            if(($webhooks->count) > 0 and (empty($this->webhookUrl) === false))
            {
                foreach ($webhooks->items as $key => $webhook)
                {
                    if($webhook->url === $this->webhookUrl)
                    {
                        $this->webhookId = $webhook->id;
                        return ['id' => $webhook->id];
                    }
                }
            }
        }
        catch(\Razorpay\Api\Errors\Error $e)
        {            
            $this->logger->info($e->getMessage());

            $this->disableWebhook();
        }
        catch(\Exception $e)
        {
            $this->logger->info($e->getMessage());

            $this->disableWebhook();
        }

        return ['id' => null];   
    }

    private function disableWebhook()
    {
        $this->config->setConfigData('enable_webhook', 0);

        try
        {
            $webhook = $this->rzp->webhook->edit([
                    "url" => $this->webhookUrl,
                    "active" => false,
                ], $this->webhookId);

            $this->logger->info("Razorpay Webhook Disabled by Admin.");
        }
        catch(\Razorpay\Api\Errors\Error $e)
        {            
            $this->logger->info($e->getMessage());
        }
        catch(\Exception $e)
        {
            $this->logger->info($e->getMessage());
        }

        $this->logger->info("Webhook disabled.");
    }

}
Magento/Model/0000755000000000000000000000000014364325176012221 5ustar  rootrootMagento/Model/ResourceModel/0000755000000000000000000000000014364325176014771 5ustar  rootrootMagento/Model/ResourceModel/OrderLink.php0000644000000000000000000000046514364325176017400 0ustar  rootroot<?php


namespace Razorpay\Magento\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

class OrderLink extends AbstractDb
{
    const TABLE_NAME = 'razorpay_sales_order';
    
    protected function _construct()
    {
        $this->_init(static::TABLE_NAME, 'entity_id');
    }
}
Magento/Model/ResourceModel/OrderLink/0000755000000000000000000000000014364325176016662 5ustar  rootrootMagento/Model/ResourceModel/OrderLink/Collection.php0000644000000000000000000000065014364325176021467 0ustar  rootroot<?php
namespace Razorpay\Magento\Model\ResourceModel\OrderLink;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;


class Collection extends AbstractCollection
{
    /**
     * Initialize resource collection
     *
     * @return void
     */
    public function _construct()
    {
        $this->_init('Razorpay\Magento\Model\OrderLink', 'Razorpay\Magento\Model\ResourceModel\OrderLink');
    }
}Magento/Model/PaymentAction.php0000644000000000000000000000111514364325176015503 0ustar  rootroot<?php 

namespace Razorpay\Magento\Model;

use \Magento\Framework\Option\ArrayInterface;

class PaymentAction implements ArrayInterface
{
    /**
     * {@inheritdoc}
     */
    public function toOptionArray()
    {
        return [
            [
                'value' => \Razorpay\Magento\Model\PaymentMethod::ACTION_AUTHORIZE,
                'label' => __('Authorize Only'),
            ],
            [
                'value' => \Razorpay\Magento\Model\PaymentMethod::ACTION_AUTHORIZE_CAPTURE,
                'label' => __('Authorize and Capture')
            ]
        ];
    }
}Magento/Model/PaymentMethod.php0000644000000000000000000005375514364325176015527 0ustar  rootroot<?php

namespace Razorpay\Magento\Model;

use Razorpay\Api\Api;
use Magento\Framework\Exception\LocalizedException;
use Magento\Sales\Model\Order\Payment\Transaction;
use Magento\Sales\Model\ResourceModel\Order\Payment\Transaction\CollectionFactory as TransactionCollectionFactory;
use Magento\Sales\Model\Order\Payment\Transaction as PaymentTransaction;
use Magento\Payment\Model\InfoInterface;
use Razorpay\Magento\Model\Config;
use Magento\Catalog\Model\Session;

/**
 * Class PaymentMethod
 * @package Razorpay\Magento\Model
 * @SuppressWarnings(PHPMD.TooManyFields)
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
 */
class PaymentMethod extends \Magento\Payment\Model\Method\AbstractMethod
{
    const CHANNEL_NAME                  = 'Magento';
    const METHOD_CODE                   = 'razorpay';
    const CONFIG_MASKED_FIELDS          = 'masked_fields';
    const CURRENCY                      = 'INR';

    /**
     * @var string
     */
    protected $_code                    = self::METHOD_CODE;

    /**
     * @var bool
     */
    protected $_canAuthorize            = true;

    /**
     * @var bool
     */
    protected $_canCapture              = true;

    /**
     * @var bool
     */
    protected $_canRefund               = true;

    /**
     * @var bool
     */
    protected $_canUseInternal          = false;        //Disable module for Magento Admin Order

    /**
     * @var bool
     */
    protected $_canUseCheckout          = true;

    /**
     * @var bool
     */
    protected $_canRefundInvoicePartial = true;

    /**
     * @var array|null
     */
    protected $requestMaskedFields      = null;

    /**
     * @var \Razorpay\Magento\Model\Config
     */
    protected $config;

    /**
     * @var \Magento\Framework\App\RequestInterface
     */
    protected $request;

    /**
     * @var TransactionCollectionFactory
     */
    protected $salesTransactionCollectionFactory;

    /**
     * @var \Magento\Framework\App\ProductMetadataInterface
     */
    protected $productMetaData;

    /**
     * @var \Magento\Directory\Model\RegionFactory
     */
    protected $regionFactory;

    /**
     * @var \Magento\Sales\Api\OrderRepositoryInterface
     */
    protected $orderRepository;

    /**
     * @param \Magento\Framework\Model\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
     * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory
     * @param \Magento\Payment\Helper\Data $paymentData
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Magento\Payment\Model\Method\Logger $logger
     * @param \Razorpay\Magento\Model\Config $config
     * @param \Magento\Framework\App\RequestInterface $request
     * @param TransactionCollectionFactory $salesTransactionCollectionFactory
     * @param \Magento\Framework\App\ProductMetadataInterface $productMetaData
     * @param \Magento\Directory\Model\RegionFactory $regionFactory
     * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
     * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
     * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
     * @param array $data
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     */
    public function __construct(
        \Magento\Framework\Model\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
        \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory,
        \Magento\Payment\Helper\Data $paymentData,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Payment\Model\Method\Logger $logger,
        \Razorpay\Magento\Model\Config $config,
        \Magento\Framework\App\RequestInterface $request,
        TransactionCollectionFactory $salesTransactionCollectionFactory,
        \Magento\Framework\App\ProductMetadataInterface $productMetaData,
        \Magento\Directory\Model\RegionFactory $regionFactory,
        \Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
        \Razorpay\Magento\Controller\Payment\Order $order,
        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        parent::__construct(
            $context,
            $registry,
            $extensionFactory,
            $customAttributeFactory,
            $paymentData,
            $scopeConfig,
            $logger,
            $resource,
            $resourceCollection,
            $data
        );
        $this->config = $config;
        $this->request = $request;
        $this->salesTransactionCollectionFactory = $salesTransactionCollectionFactory;
        $this->productMetaData = $productMetaData;
        $this->regionFactory = $regionFactory;
        $this->orderRepository = $orderRepository;

        $this->key_id = $this->config->getConfigData(Config::KEY_PUBLIC_KEY);
        $this->key_secret = $this->config->getConfigData(Config::KEY_PRIVATE_KEY);

        $this->rzp = new Api($this->key_id, $this->key_secret);

        $this->order = $order;

        $this->rzp->setHeader('User-Agent', 'Razorpay/'. $this->getChannel());
    }

    /**
     * Validate data
     *
     * @return $this
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function validate()
    {
        $info = $this->getInfoInstance();
        if ($info instanceof \Magento\Sales\Model\Order\Payment) {
            $billingCountry = $info->getOrder()->getBillingAddress()->getCountryId();
        } else {
            $billingCountry = $info->getQuote()->getBillingAddress()->getCountryId();
        }

        if (!$this->config->canUseForCountry($billingCountry)) {
            throw new LocalizedException(__('Selected payment type is not allowed for billing country.'));
        }

        return $this;
    }

    /**
     * Authorizes specified amount
     *
     * @param InfoInterface $payment
     * @param string $amount
     * @return $this
     * @throws LocalizedException
     */
    public function authorize(InfoInterface $payment, $amount)
    {
        try
        {
            /** @var \Magento\Sales\Model\Order\Payment $payment */
            $order = $payment->getOrder();
            $orderId = $order->getIncrementId();

            $request = $this->getPostData();

            $isWebhookCall = false;

            //validate RzpOrderamount with quote/order amount before signature
            $orderAmount = (int) (number_format($order->getGrandTotal() * 100, 0, ".", ""));

            $_objectManager  = \Magento\Framework\App\ObjectManager::getInstance();

            $orderLinkCollection = $_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                     ->getCollection()
                                                    ->addFilter('quote_id', $order->getQuoteId())
                                                    ->getFirstItem();

            $orderLink = $orderLinkCollection->getData();

            if((empty($request) === true) and (isset($_POST['razorpay_signature']) === true))
            {
                //set request data based on redirect flow
                $request['paymentMethod']['additional_data'] = [
                    'rzp_payment_id' => $_POST['razorpay_payment_id'],
                    'rzp_order_id' => $_POST['razorpay_order_id'],
                    'rzp_signature' => $_POST['razorpay_signature']
                ];
            }

            if((empty($request) === true) and (count($_POST) === 0))
            {
                //webhook cron call
                //update orderLink
                $isWebhookCall = true;

                if (empty($orderLink['entity_id']) === false)
                {
                    $payment_id = $orderLink['rzp_payment_id'];

                    $rzp_order_id = $orderLink['rzp_order_id'];

                    $rzpOrderAmount = $orderLink['rzp_order_amount'];

                    //set request data based on records in DB
                    $request['paymentMethod']['additional_data'] = [
                        'rzp_payment_id' => $payment_id,
                        'rzp_order_id' => $orderLink['rzp_order_id'],
                        'rzp_signature' => $orderLink['rzp_signature']
                    ];
                }

            }

            if(empty($request['payload']['payment']['entity']['id']) === false)
            {
                $payment_id = $request['payload']['payment']['entity']['id'];
                $rzp_order_id = $request['payload']['order']['entity']['id'];

                $isWebhookCall = true;
                //validate that request is from webhook only
                $this->validateWebhookSignature($request);
            }
            else
            {
                //check for GraphQL
                if(empty($request['query']) === false)
                {

                    //update orderLink
                    if (empty($orderLink['entity_id']) === false)
                    {
                        $payment_id = $orderLink['rzp_payment_id'];

                        $rzp_order_id = $orderLink['rzp_order_id'];

                        $rzp_signature = $orderLink['rzp_signature'];

                        $rzp_order_amount_actual = (int) $orderLink['rzp_order_amount'];

                        if((empty($payment_id) === true) and
                           (empty($rzp_order_id) === true) and
                           (empty($rzp_signature) === true))
                        {
                            throw new LocalizedException(__("Razorpay Payment details missing."));
                        }

                        if ($orderAmount !== $rzp_order_amount_actual)
                        {
                            $rzpOrderAmount = $order->getOrderCurrency()->formatTxt(number_format($rzp_order_amount_actual / 100, 2, ".", ""));

                            $this->_logger->critical(__("Cart order amount = %1 doesn't match with amount paid = %2", $order->getOrderCurrency()->formatTxt($order->getGrandTotal()), $rzpOrderAmount));

                            if ($this->config->isSkipOrderEnabled() === true)
                            {
                                throw new LocalizedException(__("Cart order amount = %1 doesn't match with amount paid = %2", $order->getOrderCurrency()->formatTxt($order->getGrandTotal()), $rzpOrderAmount));
                            }
                        }

                        //validate payment signature first
                        $this->validateSignature([
                            'razorpay_payment_id' => $payment_id,
                            'razorpay_order_id'   => $rzp_order_id,
                            'razorpay_signature'  => $rzp_signature
                        ]);

                        try
                        {
                            //fetch the payment from API and validate the amount
                            $payment_data = $this->rzp->payment->fetch($payment_id);
                        }
                        catch(\Razorpay\Api\Errors\Error $e)
                        {
                           $this->_logger->critical($e);
                           throw new LocalizedException(__('Razorpay Error: %1.', $e->getMessage()));
                        }

                        if($payment_data->order_id === $rzp_order_id)
                        {
                            try
                            {
                                //fetch order from API
                                $rzp_order_data = $this->rzp->order->fetch($rzp_order_id);
                            }
                            catch(\Razorpay\Api\Errors\Error $e)
                            {
                               $this->_logger->critical($e);
                               throw new LocalizedException(__('Razorpay Error: %1.', $e->getMessage()));
                            }

                            //verify order receipt
                            if($rzp_order_data->receipt !== $order->getQuoteId())
                            {
                                throw new LocalizedException(__("Not a valid Razorpay Payment"));
                            }

                            //verify currency
                            if($payment_data->currency !== $order->getOrderCurrencyCode())
                            {
                                throw new LocalizedException(__("Order Currency:(%1) not matched with payment currency:(%2)", $order->getOrderCurrencyCode(), $payment_data->currency));
                            }
                        }
                        else
                        {
                            throw new LocalizedException(__("Not a valid Razorpay Payments."));
                        }

                    }
                    else
                    {
                        throw new LocalizedException(__("Razorpay Payment details missing."));
                    }
                }
                else
                {
                    if (empty($orderLink['entity_id']) === false)
                    {
                        $rzpOrderAmount = $orderLink['rzp_order_amount'];
                        $rzp_order_id = $orderLink['rzp_order_id'];
                    }

                    // Order processing through front-end
                    if(empty($request['paymentMethod']['additional_data']['rzp_payment_id']) === false)
                    {
                        $payment_id = $request['paymentMethod']['additional_data']['rzp_payment_id'];

                        $rzp_order_id = $rzp_order_id;

                        $rzpOrderAmount = (int) $rzpOrderAmount;

                        if ($orderAmount !== $rzpOrderAmount)
                        {
                            $rzpOrderAmount = $order->getOrderCurrency()->formatTxt(number_format($rzpOrderAmount / 100, 2, ".", ""));

                            $this->_logger->critical(__("Cart order amount = %1 doesn't match with amount paid = %2", $order->getOrderCurrency()->formatTxt($order->getGrandTotal()), $rzpOrderAmount));

                            if ($this->config->isSkipOrderEnabled() === true)
                            {
                                throw new LocalizedException(__("Cart order amount = %1 doesn't match with amount paid = %2", $order->getOrderCurrency()->formatTxt($order->getGrandTotal()), $rzpOrderAmount));
                            }
                        }

                        $this->validateSignature([
                            'razorpay_payment_id' => $payment_id,
                            'razorpay_order_id'   => $rzp_order_id,
                            'razorpay_signature'  => $request['paymentMethod']['additional_data']['rzp_signature']
                        ]);
                    }
                }
            }

            if((isset($payment_id) === true) and
               (empty($payment_id) === false))
            {
                $payment->setStatus(self::STATUS_APPROVED)
                    ->setAmountPaid($amount)
                    ->setLastTransId($payment_id)
                    ->setTransactionId($payment_id)
                    ->setIsTransactionClosed(true)
                    ->setShouldCloseParentTransaction(true);

                //update the Razorpay payment with corresponding created order ID of this quote ID
                $this->updatePaymentNote($payment_id, $order, $rzp_order_id, $isWebhookCall);
            }
            else
            {
                $error = "Razorpay paymentId missing for payment verification.";

                $this->_logger->critical($error);
                throw new LocalizedException(__('Razorpay Error: %1.', $error));
            }

        }
        catch (\Exception $e)
        {
            $this->_logger->critical($e);
            throw new LocalizedException(__('Razorpay Error: %1.', $e->getMessage()));
        }

        return $this;
    }

    /**
     * Capture specified amount with authorization
     *
     * @param InfoInterface $payment
     * @param string $amount
     * @return $this
     */

    public function capture(InfoInterface $payment, $amount)
    {
        //check if payment has been authorized
        if(is_null($payment->getParentTransactionId())) {
            $this->authorize($payment, $amount);
        }

        return $this;
    }

    /**
     * Update the payment note with Magento frontend OrderID
     *
     * @param string $razorPayPaymentId
     * @param object $salesOrder
     * @param object $$rzp_order_id
     * @param object $$isWebhookCall
     */
    protected function updatePaymentNote($paymentId, $order, $rzpOrderId, $isWebhookCall)
    {
        //update the Razorpay payment with corresponding created order ID of this quote ID
        $this->rzp->payment->fetch($paymentId)->edit(
            array(
                'notes' => array(
                    'merchant_order_id' => $order->getIncrementId(),
                    'merchant_quote_id' => $order->getQuoteId()
                )
            )
        );

        //update orderLink
        $_objectManager  = \Magento\Framework\App\ObjectManager::getInstance();

        $orderLinkCollection = $_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                   ->getCollection()
                                                   ->addFieldToSelect('entity_id')
                                                   ->addFilter('quote_id', $order->getQuoteId())
                                                   ->addFilter('rzp_order_id', $rzpOrderId)
                                                   ->getFirstItem();

        $orderLink = $orderLinkCollection->getData();

        if (empty($orderLink['entity_id']) === false)
        {

            $orderLinkCollection->setRzpPaymentId($paymentId)
                                ->setIncrementOrderId($order->getIncrementId());

            if ($isWebhookCall)
            {
                $orderLinkCollection->setByWebhook(true)->save();
            }
            else
            {
                $orderLinkCollection->setByFrontend(true)->save();
            }
        }

    }

    protected function validateSignature($request)
    {
        $attributes = array(
            'razorpay_payment_id' => $request['razorpay_payment_id'],
            'razorpay_order_id'   => $request['razorpay_order_id'],
            'razorpay_signature'  => $request['razorpay_signature'],
        );

        $this->rzp->utility->verifyPaymentSignature($attributes);
    }

    /**
     * [validateWebhookSignature Used in case of webhook request for payment auth]
     * @param  array  $post
     * @return [type]
     */
    public  function validateWebhookSignature(array $post)
    {
        $webhookSecret = $this->config->getWebhookSecret();

        $postData = file_get_contents('php://input');

        $this->rzp->utility->verifyWebhookSignature($postData, $_SERVER['HTTP_X_RAZORPAY_SIGNATURE'], $webhookSecret);
    }

    protected function getPostData()
    {
        $request = file_get_contents('php://input');

        return json_decode($request, true);
    }

    /**
     * Refunds specified amount
     *
     * @param InfoInterface $payment
     * @param float $amount
     * @return $this
     * @throws LocalizedException
     */
    public function refund(InfoInterface $payment, $amount)
    {
        $order = $payment->getOrder();

        $creditmemo = $this->request->getPost('creditmemo');

        $reason = (!empty($creditmemo['comment_text'])) ? $creditmemo['comment_text'] : 'Refunded by site admin';

        $refundId = $payment->getTransactionId();

        $paymentId = substr($refundId, 0, -7);

        try
        {
            $data = array(
                'amount'    =>  (int) round($amount * 100),
                'receipt'   =>  $order->getIncrementId(),
                'notes'     =>  array(
                    'reason'                =>  $reason,
                    'order_id'              =>  $order->getIncrementId(),
                    'refund_from_website'   =>  true,
                    'source'                =>  'Magento',
                )
            );

            $refund = $this->rzp->payment
                                ->fetch( $paymentId )
                                ->refund( $data );

            $payment->setAmountPaid($amount)
                    ->setLastTransId($refund->id)
                    ->setTransactionId($refund->id)
                    ->setIsTransactionClosed(true)
                    ->setShouldCloseParentTransaction(true);
        }
        catch(\Razorpay\Api\Errors\Error $e)
        {
            $this->_logger->critical($e);
            throw new LocalizedException(__('Razorpay Error: %1.', $e->getMessage()));
        }
        catch(\Exception $e)
        {
            $this->_logger->critical($e);
            throw new LocalizedException(__('Razorpay Error: %1.', $e->getMessage()));
        }
                
        return $this;
    }

    /**
     * Format param "channel" for transaction
     *
     * @return string
     */
    protected function getChannel()
    {
        $edition = $this->productMetaData->getEdition();
        $version = $this->productMetaData->getVersion();
        return self::CHANNEL_NAME . ' ' . $edition . ' ' . $version;
    }

    /**
     * Retrieve information from payment configuration
     *
     * @param string $field
     * @param int|string|null|\Magento\Store\Model\Store $storeId
     *
     * @return mixed
     */
    public function getConfigData($field, $storeId = null)
    {
        if ('order_place_redirect_url' === $field) {
            return $this->getOrderPlaceRedirectUrl();
        }
        return $this->config->getConfigData($field, $storeId);
    }

    /**
     * Get the amount paid from RZP
     *
     * @param string $paymentId
     */
    public function getAmountPaid($paymentId)
    {
        $payment = $this->rzp->payment->fetch($paymentId);

        return $payment->amount;
    }
}
Magento/Model/Resolver/0000755000000000000000000000000014364325176014022 5ustar  rootrootMagento/Model/Resolver/PlaceRazorpayOrder.php0000644000000000000000000001136014364325176020304 0ustar  rootroot<?php
declare (strict_types = 1);

namespace Razorpay\Magento\Model\Resolver;


use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\QuoteGraphQl\Model\Cart\GetCartForUser;
use Magento\Quote\Api\CartManagementInterface;
use Razorpay\Magento\Model\PaymentMethod;

class PlaceRazorpayOrder implements ResolverInterface
{

    protected $scopeConfig;

    protected $cartManagement;

    protected $_objectManager;

    public function __construct(
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        GetCartForUser $getCartForUser,
        \Magento\Quote\Api\CartManagementInterface $cartManagement,
        PaymentMethod $paymentMethod
    ) {
        $this->scopeConfig    = $scopeConfig;
        $this->getCartForUser = $getCartForUser;
        $this->cartManagement = $cartManagement;
        $this->rzp = $paymentMethod->rzp;
        $this->_objectManager   = \Magento\Framework\App\ObjectManager::getInstance();
    }

    /**
     * @param GetCartForUser $getCartForUser
     * @inheritdoc
     */
    public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
    {
        if (empty($args['cart_id'])) {
            throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
        }

        try
        {
            $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;

            $storeId = (int) $context->getExtensionAttributes()->getStore()->getId();

            $maskedCartId = $args['cart_id'];

            $cart            = $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId);
            $receipt_id      = $cart->getId();
            $amount          = (int) (number_format($cart->getGrandTotal() * 100, 0, ".", ""));
            $payment_action  = $this->scopeConfig->getValue('payment/razorpay/payment_action', $storeScope);
            
            $payment_capture = 1;
            
            if ($payment_action === 'authorize')
            {
                $payment_capture = 0;
            }

            $order = $this->rzp->order->create([
                'amount'          => $amount,
                'receipt'         => $receipt_id,
                'currency'        => $cart->getQuoteCurrencyCode(),
                'payment_capture' => $payment_capture,
                'app_offer'       => (($cart->getBaseSubtotal() - $cart->getBaseSubtotalWithDiscount()) > 0) ? 1 : 0,
            ]);

            if (null !== $order && !empty($order->id))
            {

                $responseContent = [
                    'success'        => true,
                    'rzp_order_id'   => $order->id,
                    'order_quote_id' => $receipt_id,
                    'amount'         => number_format((float) $cart->getGrandTotal(), 2, ".", ""),
                    'currency'       => $cart->getQuoteCurrencyCode(),
                    'message'        => 'Razorpay Order created successfully'
                ];
                

                //save to razorpay orderLink
                $orderLinkCollection = $this->_objectManager
                    ->get('Razorpay\Magento\Model\OrderLink')
                    ->getCollection()
                    ->addFilter('quote_id', $receipt_id)
                    ->getFirstItem();

                $orderLinkData = $orderLinkCollection->getData();

                if (empty($orderLinkData['entity_id']) === false)
                {
                    $orderLinkCollection->setRzpOrderId($order->id)
                                        ->setRzpOrderAmount($amount)
                                        ->save();
                }
                else
                {
                    $orderLink = $this->_objectManager->create('Razorpay\Magento\Model\OrderLink');
                    $orderLink->setQuoteId($receipt_id)
                            ->setRzpOrderId($order->id)
                            ->setRzpOrderAmount($amount)
                            ->save();
                }

                return $responseContent;

            }else
            {
                return [
                    'success' => false,
                    'message' => "Razorpay Order not generated. Something went wrong",
                ];
            }
        }
        catch (\Razorpay\Api\Errors\Error $e)
        {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
        catch (\Exception $e)
        {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }
}
Magento/Model/Resolver/SetRzpPaymentDetailsOnCart.php0000644000000000000000000001147714364325176021747 0ustar  rootroot<?php

declare(strict_types=1);

namespace Razorpay\Magento\Model\Resolver;

use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\QuoteGraphQl\Model\Cart\CheckCartCheckoutAllowance;
use Magento\QuoteGraphQl\Model\Cart\GetCartForUser;
use Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart as SetPaymentMethodOnCartModel;
use Razorpay\Magento\Model\PaymentMethod;

/**
 * Mutation resolver for setting payment method for shopping cart
 */
class SetRzpPaymentDetailsOnCart implements ResolverInterface
{
    /**
     * @var GetCartForUser
     */
    private $getCartForUser;

    /**
     * @var SetPaymentMethodOnCartModel
     */
    private $setPaymentMethodOnCart;

    /**
     * @var CheckCartCheckoutAllowance
     */
    private $checkCartCheckoutAllowance;

    protected $_objectManager;



    /**
     * @param GetCartForUser $getCartForUser
     * @param SetPaymentMethodOnCartModel $setPaymentMethodOnCart
     * @param CheckCartCheckoutAllowance $checkCartCheckoutAllowance
     */
    public function __construct(
        GetCartForUser $getCartForUser,
        SetPaymentMethodOnCartModel $setPaymentMethodOnCart,
        CheckCartCheckoutAllowance $checkCartCheckoutAllowance,
        PaymentMethod $paymentMethod
    ) {
        $this->getCartForUser = $getCartForUser;
        $this->setPaymentMethodOnCart = $setPaymentMethodOnCart;
        $this->checkCartCheckoutAllowance = $checkCartCheckoutAllowance;

        $this->rzp = $paymentMethod->rzp;

        $this->_objectManager   = \Magento\Framework\App\ObjectManager::getInstance();
    }

    /**
     * @inheritdoc
     */
    public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
    {
        if (empty($args['input']['cart_id']))
        {
            throw new GraphQlInputException(__('Required parameter "cart_id" is missing.'));
        }

        $maskedCartId = $args['input']['cart_id'];

        if (empty($args['input']['rzp_payment_id']))
        {
            throw new GraphQlInputException(__('Required parameter "rzp_payment_id" is missing.'));
        }

        $rzp_payment_id = $args['input']['rzp_payment_id'];

        if (empty($args['input']['rzp_order_id']))
        {
            throw new GraphQlInputException(__('Required parameter "rzp_order_id" is missing.'));
        }

        $rzp_order_id = $args['input']['rzp_order_id'];

        if (empty($args['input']['rzp_signature']))
        {
            throw new GraphQlInputException(__('Required parameter "rzp_signature" is missing.'));
        }

        $rzp_signature = $args['input']['rzp_signature'];

        $storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
        $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId);


        try
        {
            //fetch order from API
            $rzp_order_data = $this->rzp->order->fetch($rzp_order_id);

            if($rzp_order_data->receipt !== $cart->getId())
            {
                throw new GraphQlInputException(__('Not a valid Razorpay orderID'));
            }

        }
        catch(\Razorpay\Api\Errors\Error $e)
        {
           throw new GraphQlInputException(__('Razorpay Error: %1.', $e->getMessage()));
        }

        try
        {
            //save to razorpay orderLink
            $orderLinkCollection = $this->_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                   ->getCollection()
                                                   ->addFilter('quote_id', $cart->getId())
                                                   ->getFirstItem();

            $orderLinkData = $orderLinkCollection->getData();


            if (empty($orderLinkData['entity_id']) === false)
            {
                $orderLinkCollection->setRzpPaymentId($rzp_payment_id)
                                    ->setRzpOrderId($rzp_order_id)
                                    ->setRzpSignature($rzp_signature)
                                    ->save();
            }
            else
            {
                $orderLnik = $this->_objectManager->create('Razorpay\Magento\Model\OrderLink');
                $orderLnik->setQuoteId($cart->getId())
                          ->setRzpPaymentId($rzp_payment_id)
                          ->setRzpOrderId($rzp_order_id)
                          ->setRzpSignature($rzp_signature)
                          ->save();
            }

        }
        catch (\Exception $e)
        {
            throw new GraphQlInputException(__('Razorpay Error: %1.', $e->getMessage()));
        }

        return [
            'cart' => [
                'model' => $cart,
            ],
        ];
    }
}
Magento/Model/System/0000755000000000000000000000000014364325176013505 5ustar  rootrootMagento/Model/System/Message/0000755000000000000000000000000014364325176015071 5ustar  rootrootMagento/Model/System/Message/UpgradeMessageNotification.php0000644000000000000000000000620714364325176023052 0ustar  rootroot<?php 

namespace Razorpay\Magento\Model\System\Message;

use Magento\Framework\Notification\MessageInterface;
use Razorpay\Magento\Model\Config;
use Requests;

// Include Requests only if not already defined
if (class_exists('Requests') === false)
{
  require_once __DIR__.'/../../../../Razorpay/libs/Requests-1.8.0/library/Requests.php';
}

try
{
    Requests::register_autoloader();

    if (version_compare(Requests::VERSION, '1.6.0') === -1)
    {
      throw new \Exception('Requests class found but did not match'.Requests::VERSION);
    }
}
catch (\Exception $e)
{
    throw new \Exception('Requests class found but did not match'.Requests::VERSION);
}

class UpgradeMessageNotification implements MessageInterface {

   /**
    * Message identity
    */
   const MESSAGE_IDENTITY = 'custom_system_notification';

   public $latestVersion = "";

   public $currentVersion = "";

   public $latestVersionLink = "";


  public function __construct(
        \Razorpay\Magento\Model\Config $config
    ) {

      $this->config = $config;
    }
   /**
    * Retrieve unique system message identity
    *
    * @return string
    */
   public function getIdentity()
   {
      return self::MESSAGE_IDENTITY;
   }

   /**
    * Check whether the system message should be shown
    *
    * @return bool
    */
   public function isDisplayed()
   {
      // return true will show the system notification, here you have to check your condition to display notification and base on that return true or false
      
      $disableUpgradeNotice = $this->config->getConfigData(Config::DISABLE_UPGRADE_NOTICE);
      
      $isActive = $this->config->getConfigData(Config::KEY_ACTIVE);

      if($isActive and !$disableUpgradeNotice)
      {
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();

        $this->currentVersion =  $objectManager->get('Magento\Framework\Module\ModuleList')->getOne('Razorpay_Magento')['setup_version'];

        $request = Requests::get("https://api.github.com/repos/razorpay/razorpay-magento/releases/latest");

        if($request->status_code === 200)
        {
          $razorpayLatestRelease = json_decode($request->body);

          $this->latestVersion = $razorpayLatestRelease->tag_name;

          $this->latestVersionLink = $razorpayLatestRelease->html_url;

          if(version_compare($this->currentVersion, $this->latestVersion, '<'))
          {
            return true;
          }
        
        }
      }
      
      
      return false;
   }

   /**
    * Retrieve system message text
    *
    * @return \Magento\Framework\Phrase
    */
   public function getText()
   {    
      return __('Please upgrade to the latest version of Razorpay (<a href="' . $this->latestVersionLink
                  . '" target="_blank">' . $this->latestVersion . '</a>) ');
   }

   /**
    * Retrieve system message severity
    * Possible default system message types:
    * - MessageInterface::SEVERITY_CRITICAL
    * - MessageInterface::SEVERITY_MAJOR
    * - MessageInterface::SEVERITY_MINOR
    * - MessageInterface::SEVERITY_NOTICE
    *
    * @return int
    */
   public function getSeverity()
   {
       return self::SEVERITY_NOTICE;
   }

}Magento/Model/WebhookUrl.php0000644000000000000000000000457014364325176015021 0ustar  rootroot<?php 

namespace Razorpay\Magento\Model;

use Magento\Framework\Data\Form\Element\AbstractElement;
/**
 *  Used to display webhook url link
 */
class WebhookUrl extends \Magento\Config\Block\System\Config\Form\Field
{    
    protected function _getElementHtml(AbstractElement $element)
    {

        $baseUrl = $this->_storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_WEB);

        $copyButton = "<sapn class='rzp-webhook-to-clipboard'
                                           style='background-color: #337ab7; color: white; border: none;cursor: pointer; padding: 2px 4px; text-decoration: none;display: inline-block;'>Copy Url</span>
						<script type='text/javascript'>
						//<![CDATA[
						require([
						    'jquery'
						], function ($) {
							'use strict';
						    $(function() {
						        $('.rzp-webhook-to-clipboard').click(function() {
						            var temp = $('<input>');
									$('body').append(temp);
									temp.val($('.rzp-webhook-url').text()).select();
									document.execCommand('copy');
									temp.remove();
						            $('.rzp-webhook-to-clipboard').text('Copied to clipboard');
						        });

						        $('.rzp-webhook-to-clipboard-cron').click(function() {
						            var temp = $('<input>');
									$('body').append(temp);
									temp.val($('.rzp-webhook-cron-url').text()).select();
									document.execCommand('copy');
									temp.remove();
						            $('.rzp-webhook-to-clipboard-cron').text('Copied to clipboard');
						        });
						    });
						});
						//]]>
						</script>
						";

		$copyCronButton = "<span class='rzp-webhook-to-clipboard-cron'
                                           style='background-color: #337ab7; color: white; border: none;cursor: pointer; padding: 2px 4px; text-decoration: none;display: inline-block;'>Copy Url</span>";

        $element->setComment("*Please use below url for webhook* <span style='width:300px;font-weight: bold;' class='rzp-webhook-url' >" . $baseUrl . "razorpay/payment/webhook</span><br/>" . $copyButton . "<br/>*Please use below url to <b>set cron-job for 5 mins intervals</b> to process missing order* <span style='width:300px;font-weight: bold;' class='rzp-webhook-cron-url' >" . $baseUrl . "razorpay/payment/WebhookOrderCron</span><br/>" . $copyCronButton );
                
        return $element->getElementHtml();

    }
}
Magento/Model/WebhookEvents.php0000644000000000000000000000076214364325176015522 0ustar  rootroot<?php 

namespace Razorpay\Magento\Model;

use \Magento\Framework\Option\ArrayInterface;

class WebhookEvents implements ArrayInterface
{
    /**
     * {@inheritdoc}
     */
    public function toOptionArray()
    {
        return [
            [
                'value' => "order.paid",
                'label' => __('order.paid'),
            ],
            [
                'value' => "payment.authorized",
                'label' => __('payment.authorized'),
            ],
        ];
    }
}Magento/Model/CheckoutFactory.php0000644000000000000000000000145214364325176016031 0ustar  rootroot<?php

namespace Razorpay\Magento\Model;

class CheckoutFactory
{
    /**
     * Object Manager instance
     *
     * @var \Magento\Framework\ObjectManagerInterface
     */
    protected $_objectManager = null;

    /**
     * Factory constructor
     *
     * @param \Magento\Framework\ObjectManagerInterface $objectManager
     */
    public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager)
    {
        $this->_objectManager = $objectManager;
    }

    /**
     * Create class instance with specified parameters
     *
     * @param string $className
     * @param array $data
     * @return \Razorpay\Magento\Model\PaymentMethod
     */
    public function create($className, array $data = [])
    {
        return $this->_objectManager->create($className, $data);
    }
}
Magento/Model/OrderLink.php0000644000000000000000000000050314364325176014621 0ustar  rootroot<?php

namespace Razorpay\Magento\Model;

use Magento\Cron\Exception;
use Magento\Framework\Model\AbstractModel;

class OrderLink extends AbstractModel
{
    /**
     * @return void
     */
    protected function _construct()
    {
        $this->_init(\Razorpay\Magento\Model\ResourceModel\OrderLink::class);
    }
    
}
Magento/Model/ConfigProvider.php0000644000000000000000000000427714364325176015664 0ustar  rootroot<?php

namespace Razorpay\Magento\Model;

use Magento\Payment\Helper\Data as PaymentHelper;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Checkout\Model\ConfigProviderInterface;

class ConfigProvider implements ConfigProviderInterface
{
    /**
     * @var string[]
     */
    protected $methodCode = PaymentMethod::METHOD_CODE;

    /**
     * @var \Razorpay\Magento\Model\Config
     */
    protected $config;

    /**
     * @var \Magento\Checkout\Model\Session
     */
    protected $checkoutSession;

    /**
     * @var \Magento\Customer\Model\Session
     */
    protected $customerSession;

    /**
     * Url Builder
     *
     * @var \Magento\Framework\Url
     */
    protected $urlBuilder;

    /**
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Framework\Url $urlBuilder
     */
    public function __construct(
        \Magento\Framework\View\Asset\Repository $assetRepo,
        \Magento\Framework\App\RequestInterface $request,
        \Magento\Framework\Url $urlBuilder,
        \Psr\Log\LoggerInterface $logger,
        PaymentHelper $paymentHelper,
        \Razorpay\Magento\Model\Config $config,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Magento\Customer\Model\Session $customerSession
    ) {
        $this->assetRepo = $assetRepo;
        $this->request = $request;
        $this->urlBuilder = $urlBuilder;
        $this->logger = $logger;
        $this->methodCode = PaymentMethod::METHOD_CODE;
        $this->method = $paymentHelper->getMethodInstance(PaymentMethod::METHOD_CODE);
        $this->config = $config;
        $this->checkoutSession = $checkoutSession;
        $this->customerSession = $customerSession;
    }

    /**
     * @return array|void
     */
    public function getConfig()
    {
        if (!$this->config->isActive()) {
            return [];
        }

        $config = [
            'payment' => [
                'razorpay' => [
                    'merchant_name' => $this->config->getMerchantNameOverride(),
                    'key_id'    => $this->config->getKeyId()
                ],
            ],
        ];

        return $config;
    }
}
Magento/Model/LogHandler.php0000644000000000000000000000263414364325176014756 0ustar  rootroot<?php 

namespace Razorpay\Magento\Model;

use Magento\Framework\Filesystem\DriverInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
/**
 *  Used to display webhook url link
 */
class LogHandler extends \Magento\Framework\Logger\Handler\Base
{    
     /**
     * Logging level
     * @var int
     */
    protected $loggerType = Logger::INFO;

    /**
     * File name
     * @var string
     */
    public $fileName = '';
    /**
     * File name
     * @var string
     */
    public $cutomfileName = 'NO_PATH';
    /**
     * @var TimezoneInterface
     */
    protected $_localeDate;

    public function __construct(
        DriverInterface $filesystem,
        \Magento\Framework\Filesystem $corefilesystem,
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
        $filePath = null
    ) {
        $this->_localeDate = $localeDate;
        $corefilesystem= $corefilesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR); 
        $logpath = $corefilesystem->getAbsolutePath('log/Razorpay/');


        // Custom log file name for each day because log will be full for optimization 
        $filename = 'rzp_'.Date('Y_m_d').'.log';

        $filepath = $logpath . $filename;
        
        $this->cutomfileName = $filepath;

        parent::__construct(
            $filesystem,
            $filepath
        );

    }
}
Magento/Model/Config.php0000644000000000000000000000753714364325176014153 0ustar  rootroot<?php

namespace Razorpay\Magento\Model;

use \Magento\Framework\App\Config\ScopeConfigInterface;
use \Magento\Framework\App\Config\Storage\WriterInterface;

class Config
{
    const KEY_ALLOW_SPECIFIC = 'allowspecific';
    const KEY_SPECIFIC_COUNTRY = 'specificcountry';
    const KEY_ACTIVE = 'active';
    const KEY_PUBLIC_KEY = 'key_id';
    const KEY_PRIVATE_KEY = 'key_secret';
    const KEY_MERCHANT_NAME_OVERRIDE = 'merchant_name_override';
    const KEY_PAYMENT_ACTION = 'payment_action';
    const ENABLE_WEBHOOK = 'enable_webhook';
    const WEBHOOK_SECRET = 'webhook_secret';
    const WEBHOOK_WAIT_TIME = 'webhook_wait_time';
    const DISABLE_UPGRADE_NOTICE = 'disable_upgrade_notice';
    const SKIP_AMOUNT_MISMATCH_ORDER = 'skip_amount_mismatch_order';

    /**
     * @var string
     */
    protected $methodCode = 'razorpay';

    /**
     * @var ScopeConfigInterface
     */
    protected $scopeConfig;

    protected $configWriter;

    /**
     * @var int
     */
    protected $storeId = null;

    /**
     * @param ScopeConfigInterface $scopeConfig
     */
    public function __construct(
        ScopeConfigInterface $scopeConfig,
        WriterInterface $configWriter
    ) {
        $this->scopeConfig = $scopeConfig;
        $this->configWriter = $configWriter;
    }

    /**
     * @return string
     */
    public function getMerchantNameOverride()
    {
        return $this->getConfigData(self::KEY_MERCHANT_NAME_OVERRIDE);
    }

    public function getKeyId()
    {
        return $this->getConfigData(self::KEY_PUBLIC_KEY);
    }

    public function isWebhookEnabled()
    {
        return (bool) (int) $this->getConfigData(self::ENABLE_WEBHOOK, $this->storeId);
    }

    public function getWebhookSecret()
    {
        return $this->getConfigData(self::WEBHOOK_SECRET);
    }
    
    public function getPaymentAction()
    {
        return $this->getConfigData(self::KEY_PAYMENT_ACTION);
    }

    public function isSkipOrderEnabled()
    {
        return (bool) (int) $this->getConfigData(self::SKIP_AMOUNT_MISMATCH_ORDER, $this->storeId);
    }

    /**
     * @param int $storeId
     * @return $this
     */
    public function setStoreId($storeId)
    {
        $this->storeId = $storeId;
        return $this;
    }

    /**
     * Retrieve information from payment configuration
     *
     * @param string $field
     * @param null|string $storeId
     *
     * @return mixed
     */
    public function getConfigData($field, $storeId = null)
    {
        if ($storeId == null) {
            $storeId = $this->storeId;
        }

        $code = $this->methodCode;

        $path = 'payment/' . $code . '/' . $field;
        return $this->scopeConfig->getValue($path, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeId);
    }

    /**
     * Set information from payment configuration
     *
     * @param string $field
     * @param string $value
     * @param null|string $storeId
     *
     * @return mixed
     */
    public function setConfigData($field, $value)
    {
        $code = $this->methodCode;

        $path = 'payment/' . $code . '/' . $field;

        return $this->configWriter->save($path, $value);
    }

    /**
     * @return bool
     */
    public function isActive()
    {
        return (bool) (int) $this->getConfigData(self::KEY_ACTIVE, $this->storeId);
    }

    /**
     * To check billing country is allowed for the payment method
     *
     * @param string $country
     * @return bool
     */
    public function canUseForCountry($country)
    {
        /*
        for specific country, the flag will set up as 1
        */
        if ($this->getConfigData(self::KEY_ALLOW_SPECIFIC) == 1) {
            $availableCountries = explode(',', $this->getConfigData(self::KEY_SPECIFIC_COUNTRY));
            if (!in_array($country, $availableCountries)) {
                return false;
            }
        }

        return true;
    }
}
Magento/Cron/0000755000000000000000000000000014364325176012062 5ustar  rootrootMagento/Cron/WebhookOrderCron.php0000644000000000000000000002756514364325176016026 0ustar  rootroot<?php
namespace Razorpay\Magento\Cron;

use Psr\Log\LoggerInterface;
use Razorpay\Magento\Model\Config;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\DataObject;

class WebhookOrderCron {
    protected $logger;

    public function __construct(
        LoggerInterface $logger,
        \Razorpay\Magento\Model\Config $config,
        \Magento\Quote\Model\QuoteRepository $quoteRepository,
        \Magento\Sales\Api\Data\OrderInterface $order,
        \Magento\Quote\Model\QuoteManagement $quoteManagement,
        \Magento\Store\Model\StoreManagerInterface $storeManagement,
        \Magento\Framework\Event\ManagerInterface $eventManager
    )
    {
        $this->logger = $logger;

        $this->config = $config;

        $this->order           = $order;

        $this->objectManagement   = \Magento\Framework\App\ObjectManager::getInstance();
        $this->quoteManagement    = $quoteManagement;
        
        $this->quoteRepository    = $quoteRepository;
        $this->storeManagement    = $storeManagement;
       
        $this->eventManager       = $eventManager;
    }

   /**
    * Write to system.log
    *
    * @return void
    */
    public function execute()
    {   
        $this->logger->info("Razorpay Webhook Order Cron job processing started.");
        try
        {

            $orderLinkCollection = $this->objectManagement->get('Razorpay\Magento\Model\OrderLink')
                                                        ->getCollection()
                                                        ->addFilter('order_placed', 0)
                                                        ->addFieldToFilter('webhook_count',["lt" => 4])
                                                        ->addFieldToFilter('webhook_first_notified_at',["notnull" => true])
                                                        ->addFieldToFilter('amount_paid',["notnull" => true])
                                                        ->setOrder('webhook_first_notified_at')
                                                        ->setPageSize(5);

            $orderLink = $orderLinkCollection->getData();

            if(count($orderLink) > 0)
            {
                foreach ($orderLink as $orderData)
                {
                     $this->createOrder($orderData);  
                }
            }
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook Order Cron: Quote submitted for order creation failed with error: ". $e->getMessage());
            return;
        }
        $this->logger->info("Razorpay Webhook Order Cron job completed.");
    }

    /**
     * Order Paid create
     * 
     * @param array $orderData
     */
    protected function createOrder($orderData = array())
    {

        $quoteId    = $orderData['quote_id'];
        $entityId   = $orderData['entity_id'];

        try
        {
            
            $orderLinkCollection = $this->objectManagement->get('Razorpay\Magento\Model\OrderLink')
                                                        ->getCollection()
                                                        ->addFilter('entity_id', $entityId)
                                                        ->getFirstItem();
            $orderLink = $orderLinkCollection->getData();
            
            $paymentId = $orderLink['rzp_payment_id'];

            $webhookWaitTime = $this->config->getConfigData(Config::WEBHOOK_WAIT_TIME) ? $this->config->getConfigData(Config::WEBHOOK_WAIT_TIME) : 300;

            //ignore webhook call for some time as per config, from first webhook call
            if ((time() - $orderLinkCollection->getWebhookFirstNotifiedAt()) < $webhookWaitTime)
            {
                $this->logger->info(__("Razorpay Webhook Cron-1: Order processing is active for quoteID: $quoteId and Razorpay payment_id(:$paymentId) and webhook attempt: %1", ($orderLink['webhook_count'] + 1)));
                
                return;
            }

            $orderLinkCollection->setWebhookCount($orderLink['webhook_count'] + 1)
                                ->save();

            //validate if the quote Order is still active
            $quote = $this->quoteRepository->get($quoteId);

            //exit if quote is not active
            if (!$quote->getIsActive())
            {
                $this->logger->info("Razorpay Webhook Cron: Quote order is inactive for quoteID: $quoteId and Razorpay payment_id(:$paymentId)");

                return;
            }

            //validate amount before placing order
           $quoteAmount    = (int) (number_format($quote->getGrandTotal() * 100, 0, ".", ""));
           $rzpOrderAmount = (int) (number_format($orderLink['rzp_order_amount'], 0, ".", ""));

            if ($quoteAmount !== $rzpOrderAmount)
            {
                $this->logger->critical("Razorpay Webhook Cron: Amount processed for payment doesn't match with store order amount for Razorpay payment_id(:$paymentId) and quote (:$quoteId)");

                if ($this->config->isSkipOrderEnabled() === true)
                {
                    return;
                }
            }

            # fetch the related sales order and verify the payment ID with rzp payment id
            # To avoid duplicate order entry for same quote
            $collection = $this->objectManagement->get('Magento\Sales\Model\Order')
                                               ->getCollection()
                                               ->addFieldToSelect('entity_id')
                                               ->addFilter('quote_id', $quoteId)
                                               ->getFirstItem();

            $salesOrder = $collection->getData();

            if (empty($salesOrder['entity_id']) === false)
            {
                $order = $this->order->load($salesOrder['entity_id']);
                $orderRzpPaymentId = $order->getPayment()->getLastTransId();

                if ($orderRzpPaymentId === $paymentId)
                {
                    $this->logger->info("Razorpay Webhook Cron: Sales Order and payment already exist for Razorpay payment_id(:$paymentId)");

                    return;
                }
            }

            $quote = $this->getQuoteObject($orderLink, $quoteId);

            $this->logger->info("Razorpay Webhook Cron: Order creation started with quoteID:$quoteId.");

            //validate if the quote Order is still active
            $quoteUpdated = $this->quoteRepository->get($quoteId);

            //exit if quote is not active
            if (!$quoteUpdated->getIsActive())
            {
                $this->logger->info("Razorpay Webhook Cron: Quote order is inactive for quoteID: $quoteId and Razorpay payment_id(:$paymentId)");

                return;
            }

            //verify Rzp OrderLink status
            $orderLinkCollection = $this->objectManagement->get('Razorpay\Magento\Model\OrderLink')
                                                       ->getCollection()
                                                       ->addFilter('entity_id', $entityId)
                                                       ->getFirstItem();

            $orderLink = $orderLinkCollection->getData();

            if (empty($orderLink['entity_id']) === false)
            {
                if ($orderLink['order_placed'])
                {
                    $this->logger->info(__("Razorpay Webhook Cron: Quote order is inactive for quoteID: $quoteId and Razorpay payment_id(:$paymentId) with Maze OrderID (:%1) ", $orderLink['increment_order_id']));

                    return;
                }

                $amount = $orderLink['amount_paid'];
            }
           

            $this->logger->info("Razorpay Webhook Cron: Quote submitted for order creation with quoteID:$quoteId.");

            $order = $this->quoteManagement->submit($quote);

            $payment = $order->getPayment();

            $this->logger->info("Razorpay Webhook Cron: Adding payment to order for quoteID:$quoteId.");

            $payment->setAmountPaid($amount)
                    ->setLastTransId($paymentId)
                    ->setTransactionId($paymentId)
                    ->setIsTransactionClosed(true)
                    ->setShouldCloseParentTransaction(true);

            //set razorpay webhook fields
            $order->setByRazorpayWebhook(1);

            $order->save();

            //disable the quote
            $quote->setIsActive(0)->save();

            //dispatch the "razorpay_webhook_order_placed_after" event
            $eventData = [
                            'raorpay_payment_id' => $paymentId,
                            'magento_quote_id' => $quoteId,
                            'magento_order_id' => $order->getEntityId(),
                            'amount_captured' => $amount
                         ];

            $transport = new DataObject($eventData);

            $this->eventManager->dispatch(
                'razorpay_webhook_order_placed_after',
                [
                    'context'   => 'razorpay_webhook_order',
                    'payment'   => $paymentId,
                    'transport' => $transport
                ]
            );

            $this->logger->info("Razorpay Webhook Cron Processed successfully for Razorpay payment_id(:$paymentId): and quoteID(: $quoteId) and OrderID(: ". $order->getEntityId() .")");
            return;
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook Cron: Quote submitted for order creation with quoteID:$quoteId failed with error: ". $e->getMessage());
            return;
        }
   }

    protected function getQuoteObject($post, $quoteId)
    {
        try
        {
            $quote = $this->quoteRepository->get($quoteId);

            $firstName = $quote->getBillingAddress()->getFirstname() ?? 'null';
            $lastName  = $quote->getBillingAddress()->getLastname() ?? 'null';
            $email     = $quote->getBillingAddress()->getEmail() ?? $post['email'];

            $quote->getPayment()->setMethod(PaymentMethod::METHOD_CODE);

            $store = $quote->getStore();

            if(empty($store) === true)
            {
                $store = $this->storeManagement->getStore();
            }

            $websiteId = $store->getWebsiteId();

            $customer = $this->objectManagement->create('Magento\Customer\Model\Customer');

            $customer->setWebsiteId($websiteId);

            //get customer from quote , otherwise from payment email
            $customer = $customer->loadByEmail($email);

            //if quote billing address doesn't contains address, set it as customer default billing address
            if ((empty($quote->getBillingAddress()->getFirstname()) === true) and
                (empty($customer->getEntityId()) === false))
            {
                $quote->getBillingAddress()->setCustomerAddressId($customer->getDefaultBillingAddress()['id']);
            }

            //If need to insert new customer as guest
            if ((empty($customer->getEntityId()) === true) or
                (empty($quote->getBillingAddress()->getCustomerId()) === true))
            {
                $quote->setCustomerFirstname($firstName);
                $quote->setCustomerLastname($lastName);
                $quote->setCustomerEmail($email);
                $quote->setCustomerIsGuest(true);
            }

            //skip address validation as some time billing/shipping address not set for the quote
            $quote->getBillingAddress()->setShouldIgnoreValidation(true);
            $quote->getShippingAddress()->setShouldIgnoreValidation(true);

            $quote->setStore($store);

            $quote->collectTotals();

            $quote->save();

            return $quote;
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook Cron: Unable to update/get quote with quoteID:$quoteId, failed with error: ". $e->getMessage());
            return;
        }
    }
}
Magento/registration.php0000644000000000000000000000024614364325176014406 0ustar  rootroot<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Razorpay_Magento',
    __DIR__
);
Magento/view/0000755000000000000000000000000014364325176012133 5ustar  rootrootMagento/view/frontend/0000755000000000000000000000000014364325176013752 5ustar  rootrootMagento/view/frontend/web/0000755000000000000000000000000014364325176014527 5ustar  rootrootMagento/view/frontend/web/template/0000755000000000000000000000000014364325176016342 5ustar  rootrootMagento/view/frontend/web/template/payment/0000755000000000000000000000000014364325176020017 5ustar  rootrootMagento/view/frontend/web/template/payment/razorpay-form.html0000644000000000000000000000341114364325176023514 0ustar  rootroot<div class="payment-method" data-bind="css: {'_active': (getCode() == isChecked())}">
    <div class="payment-method-title field choice">
        <input type="radio"
               name="payment[method]"
               class="radio"
               data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/>
        <label data-bind="attr: {'for': getCode()}" class="label">
            <img src="https://cdn.razorpay.com/logo.png" width="20%" alt="Razorpay" class="payment-icon"/>
        </label>
    </div>

    <div class="payment-method-content">
        <!-- ko foreach: getRegion('messages') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->
        <div class="payment-method-billing-address">
            <!-- ko foreach: $parent.getRegion(getBillingAddressFormName()) -->
            <!-- ko template: getTemplate() --><!-- /ko -->
            <!--/ko-->
        </div>
        <div class="checkout-agreements-block">
            <!-- ko foreach: $parent.getRegion('before-place-order') -->
                <!-- ko template: getTemplate() --><!-- /ko -->
            <!--/ko-->
        </div>
        <div class="actions-toolbar">
            <div class="primary">
                <button class="action primary checkout"
                    type="submit"
                    data-bind="
                    click: preparePayment,
                    attr: {title: $t('Pay with Razorpay')},
                    css: {disabled: !isPlaceOrderActionAllowed()},
                    enable: (getCode() == isChecked())
                    ">
                    <span data-bind="i18n: 'Pay with Razorpay'"></span>
                </button>
            </div>
        </div>
    </div>
</div>
Magento/view/frontend/web/js/0000755000000000000000000000000014364325176015143 5ustar  rootrootMagento/view/frontend/web/js/view/0000755000000000000000000000000014364325176016115 5ustar  rootrootMagento/view/frontend/web/js/view/payment/0000755000000000000000000000000014364325176017572 5ustar  rootrootMagento/view/frontend/web/js/view/payment/method-renderer/0000755000000000000000000000000014364325176022656 5ustar  rootrootMagento/view/frontend/web/js/view/payment/method-renderer/razorpay-method.js0000644000000000000000000003132014364325176026340 0ustar  rootrootdefine(
    [
        'Magento_Checkout/js/view/payment/default',
        'Magento_Checkout/js/model/quote',
        'jquery',
        'ko',
        'Magento_Checkout/js/model/payment/additional-validators',
        'Magento_Checkout/js/action/set-payment-information',
        'mage/url',
        'Magento_Customer/js/model/customer',
        'Magento_Checkout/js/action/place-order',
        'Magento_Checkout/js/model/full-screen-loader',
        'Magento_Ui/js/model/messageList',
        'Magento_Checkout/js/model/shipping-save-processor',
        'Magento_Customer/js/customer-data',
    ],
    function (Component, quote, $, ko, additionalValidators, setPaymentInformationAction, url, customer, placeOrderAction, fullScreenLoader, messageList, shippingSaveProcessor, customerData) {
        'use strict';

        return Component.extend({
            defaults: {
                template: 'Razorpay_Magento/payment/razorpay-form',
                razorpayDataFrameLoaded: false,
                rzp_response: {}
            },
            getMerchantName: function() {
                return window.checkoutConfig.payment.razorpay.merchant_name;
            },

            getKeyId: function() {
                return window.checkoutConfig.payment.razorpay.key_id;
            },

            context: function() {
                return this;
            },

            isShowLegend: function() {
                return true;
            },

            getCode: function() {
                return 'razorpay';
            },

            isActive: function() {
                return true;
            },

            isAvailable: function() {
                return this.razorpayDataFrameLoaded;
            },

            handleError: function (error) {
                if (_.isObject(error)) {
                    this.messageContainer.addErrorMessage(error);
                } else {
                    this.messageContainer.addErrorMessage({
                        message: error
                    });
                }
            },

            initObservable: function() {
                var self = this._super();              //Resolves UI Error on Checkout


                if(!self.razorpayDataFrameLoaded) {
                    $.getScript("https://checkout.razorpay.com/v1/checkout.js", function() {
                        self.razorpayDataFrameLoaded = true;
                    });
                }

                return self;
            },

            /**
             * @override
             */
             /** Process Payment */
            preparePayment: function (context, event) {

                if(!additionalValidators.validate()) {   //Resolve checkout aggreement accept error
                    return false;
                }

                var self = this,
                    billing_address,
                    rzp_order_id;

                fullScreenLoader.startLoader();
                this.messageContainer.clear();

                this.amount = quote.totals()['base_grand_total'] * 100;
                billing_address = quote.billingAddress();

                this.user = {
                    name: billing_address.firstname + ' ' + billing_address.lastname,
                    contact: billing_address.telephone,
                };

                if (!customer.isLoggedIn()) {
                    this.user.email = quote.guestEmail;
                }
                else
                {
                    this.user.email = customer.customerData.email;
                }

                this.isPaymentProcessing = $.Deferred();

                $.when(this.isPaymentProcessing).done(
                    function () {
                        self.placeOrder();
                    }
                ).fail(
                    function (result) {
                        self.handleError(result);
                    }
                );

                self.getRzpOrderId();

                return;
            },

            getRzpOrderId: function () {
                var self = this;

                //update shipping and billing before order into quotes
                if(!quote.isVirtual()) {
                    shippingSaveProcessor.saveShippingInformation().success(
                        function (response) {
                            self.createRzpOrder();
                        }
                    ).fail(
                        function (response) {
                            fullScreenLoader.stopLoader();
                            self.isPaymentProcessing.reject(response.message);
                        }
                    );
                } else {
                    self.createRzpOrder();
                }

            },
            createRzpOrder: function(){
                var self = this;

                $.ajax({
                    type: 'POST',
                    url: url.build('razorpay/payment/order?' + Math.random().toString(36).substring(10)),
                    data: {
                        email: this.user.email,
                        billing_address: JSON.stringify(quote.billingAddress())
                    },

                    /**
                     * Success callback
                     * @param {Object} response
                     */
                    success: function (response) {
                        fullScreenLoader.stopLoader();
                        if (response.success) {
                            if (response.is_hosted) {
                                self.renderHosted(response);
                            } else {
                                self.renderIframe(response);
                            }
                        } else {
                            self.isPaymentProcessing.reject(response.message);
                        }
                    },


                    /**
                     * Error callback
                     * @param {*} response
                     */
                    error: function (response) {
                        fullScreenLoader.stopLoader();
                        self.isPaymentProcessing.reject(response.message);
                    }
                });
            },
            createInputFieldsFromOptions: function (options, form) {
                var self = this;

                function visitNestedOption(options, parentKey) {
                    for (let curKey in options) {
                      if (options.hasOwnProperty(curKey)) {
                        const value = options[curKey];
                        let prepareKey = parentKey ? `${parentKey}[${curKey}]` : curKey;

                        if (typeof value === 'object') {
                          visitNestedOption(value, prepareKey);
                        } else {
                          // Exception: Rename key -> key_id (merchant key)
                          if (prepareKey === 'key') {
                            prepareKey = 'key_id';
                          }

                          form.appendChild(self.createHiddenInput(prepareKey, value));
                        }
                      }
                    }
                }
              visitNestedOption(options);
            },

            createHiddenInput: function(key, value) {
              var input = document.createElement('input');

              input.type = 'hidden';
              input.name = key;
              input.value = value;

              return input;
            },

            renderHosted: function(data) {
                var self = this;

                this.merchant_order_id = data.order_id;

                var opts = {
                    key: self.getKeyId(),
                    name: self.getMerchantName(),
                    amount: data.amount,
                    order_id: data.rzp_order,
                    notes: {
                        merchant_order_id: '',
                        merchant_quote_id: data.order_id
                    },
                    prefill: {
                        name: this.user.name,
                        contact: this.user.contact,
                        email: this.user.email
                    },
                    callback_url: url.build('razorpay/payment/callback?order_id=' + data.order_id),
                    cancel_url  : url.build('checkout/cart'),
                    _: {
                        integration: 'magento',
                        integration_version: data.module_version,
                        integration_parent_version: data.maze_version,
                    }
                }
                const options = JSON.parse(JSON.stringify(opts));

                var form = document.createElement('form'),
                    method = 'POST',
                    input,
                    key;

                form.method = method;
                form.action = data.embedded_url;

                self.createInputFieldsFromOptions(options, form);

                document.body.appendChild(form);

                customerData.invalidate(['cart']);

                form.submit();
            },

            checkRzpOrder: function (data) {
                var self = this;

                $.ajax({
                    type: 'POST',
                    url: url.build('razorpay/payment/order?' + Math.random().toString(36).substring(10)),
                    data: "order_check=1",

                    /**
                     * Success callback
                     * @param {Object} response
                     */
                    success: function (response) {
                        //fullScreenLoader.stopLoader();
                        if (response.success) {
                            if(response.order_id){
                                $(location).attr('href', 'onepage/success?' + Math.random().toString(36).substring(10));
                            }else{
                                fullScreenLoader.startLoader();
                                setTimeout(function(){ self.checkRzpOrder(data); }, 1500);
                            }
                        } else {
                            self.placeOrder(data);
                        }
                    },

                    /**
                     * Error callback
                     * @param {*} response
                     */
                    error: function (response) {
                        fullScreenLoader.stopLoader();
                        self.isPaymentProcessing.reject(response.message);
                    }
                });
            },

            renderIframe: function(data) {
                var self = this;

                this.merchant_order_id = data.order_id;

                var options = {
                    key: self.getKeyId(),
                    name: self.getMerchantName(),
                    amount: data.amount,
                    handler: function (data) {
                        self.rzp_response = data;
                        fullScreenLoader.startLoader();
                        self.checkRzpOrder(data);
                        fullScreenLoader.stopLoader();
                     },
                    order_id: data.rzp_order,
                    modal: {
                        ondismiss: function() {
                            self.isPaymentProcessing.reject("Payment Closed");
                        }
                    },
                    notes: {
                        merchant_order_id: '',
                        merchant_quote_id: data.order_id
                    },
                    prefill: {
                        name: this.user.name,
                        contact: this.user.contact,
                        email: this.user.email
                    },
                    callback_url: url.build('razorpay/payment/callback?order_id=' + data.order_id),
                    _: {
                        integration: 'magento',
                        integration_version: data.module_version,
                        integration_parent_version: data.maze_version,
                    }
                };

                if (data.quote_currency !== 'INR')
                {
                    options.display_currency = data.quote_currency;
                    options.display_amount = data.quote_amount;
                }

                this.rzp = new Razorpay(options);

                customerData.invalidate(['cart']);

                this.rzp.open();
            },

            getData: function() {
                return {
                    "method": this.item.method,
                    "po_number": null,
                    "additional_data": {
                        rzp_payment_id: this.rzp_response.razorpay_payment_id,
                        order_id: this.merchant_order_id,
                        rzp_signature: this.rzp_response.razorpay_signature
                    }
                };
            }
        });
    }


);
Magento/view/frontend/web/js/view/payment/razorpay-payments.js0000644000000000000000000000064014364325176023635 0ustar  rootrootdefine(
    [
        'uiComponent',
        'Magento_Checkout/js/model/payment/renderer-list'
    ],
    function (Component, rendererList) {
        'use strict';
        rendererList.push({
            type: 'razorpay',
            component: 'Razorpay_Magento/js/view/payment/method-renderer/razorpay-method'
        });
        /** Add view logic here if needed */
        return Component.extend({});
    }
);
Magento/view/frontend/requirejs-config.js0000644000000000000000000000016314364325176017564 0ustar  rootrootvar config = {
    map: {
        '*': {
            transparent: 'Magento_Payment/transparent'
        }
    }
};
Magento/view/frontend/layout/0000755000000000000000000000000014364325176015267 5ustar  rootrootMagento/view/frontend/layout/checkout_index_index.xml0000644000000000000000000000555214364325176022203 0ustar  rootroot<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="steps" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="billing-step" xsi:type="array">
                                            <item name="component" xsi:type="string">uiComponent</item>
                                            <item name="children" xsi:type="array">
                                                <item name="payment" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="renders" xsi:type="array">
                                                            <!-- merge payment method renders here -->
                                                            <item name="children" xsi:type="array">
                                                                <item name="razorpay-payments" xsi:type="array">
                                                                    <item name="component" xsi:type="string">Razorpay_Magento/js/view/payment/razorpay-payments</item>
                                                                    <item name="methods" xsi:type="array">
                                                                        <item name="razorpay" xsi:type="array">
                                                                            <item name="isBillingAddressRequired" xsi:type="boolean">true</item>
                                                                        </item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>
Magento/Setup/0000755000000000000000000000000014364325176012261 5ustar  rootrootMagento/Setup/UpgradeSchema.php0000644000000000000000000001554214364325176015511 0ustar  rootroot<?php

namespace Razorpay\Magento\Setup;

use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Razorpay\Magento\Model\ResourceModel\OrderLink;

class UpgradeSchema implements  UpgradeSchemaInterface
{
    public function upgrade(SchemaSetupInterface $setup,
                            ModuleContextInterface $context
                        )
    {
        $setup->startSetup();

        $table = $setup->getConnection()->newTable($setup->getTable(OrderLink::TABLE_NAME));

        $table
            ->addColumn(
                'entity_id',
                Table::TYPE_INTEGER,
                null,
                [
                    'identity' => true,
                    'unsigned' => true,
                    'primary'  => true,
                    'nullable' => false
                ]
            )
            ->addColumn(
                'quote_id',
                Table::TYPE_INTEGER,
                [
                    'identity' => true,
                    'unique'   => true,
                    'nullable' => false
                ]
            )
            ->addColumn(
                'order_id',
                Table::TYPE_INTEGER,
                [
                    'nullable' => true
                ]
            )
            ->addColumn(
                'increment_order_id',
                Table::TYPE_TEXT,
                32,
                [
                    'nullable' => true
                ]
            )
            ->addColumn(
                'rzp_order_id',
                Table::TYPE_TEXT,
                25,
                [
                    'nullable' => true
                ]
            )
            ->addColumn(
                'rzp_payment_id',
                Table::TYPE_TEXT,
                25,
                [
                    'nullable' => true
                ]
            )
            ->addColumn(
                'rzp_signature',
                Table::TYPE_TEXT,
                225,
                [
                    'nullable' => true
                ]
            )
            ->addColumn(
                'rzp_order_amount',
                Table::TYPE_INTEGER,
                20,
                [
                    'nullable' => true,
                    'comment'  => 'RZP order amount'
                ]
            )
            ->addColumn(
                'by_webhook',
                Table::TYPE_BOOLEAN,
                1,
                [
                    'nullable' => false,
                    'default' => 0
                ]
            )
            ->addColumn(
                'by_frontend',
                Table::TYPE_BOOLEAN,
                1,
                [
                    'nullable' => false,
                    'default' => 0
                ]
            )
            ->addColumn(
                'webhook_count',
                Table::TYPE_SMALLINT,
                3,
                [
                    'nullable' => false,
                    'default' => 0
                ]
            )
            ->addColumn(
                'order_placed',
                Table::TYPE_BOOLEAN,
                1,
                [
                    'nullable' => false,
                    'default' => 0
                ]
            )
            ->addColumn(
                'webhook_first_notified_at',
                Table::TYPE_BIGINT,
                [
                    'nullable' => true
                ]
            )
            ->addColumn(
                'amount_paid',
                Table::TYPE_INTEGER,
                20,
                [
                    'nullable' => true,
                    'comment'  => 'Actual paid amount'
                ]
            )
            ->addColumn(
                'email',
                Table::TYPE_TEXT,
                255,
                [
                    'nullable' => true,
                    'comment'  => 'payment email'
                ]
            )
            ->addColumn(
                'contact',
                Table::TYPE_TEXT,
                25,
                [
                    'nullable' => true,
                    'comment'  => 'payment contact'
                ]
            )
            ->addIndex(
                'quote_id',
                ['quote_id', 'rzp_payment_id'],
                [
                    'type'      => AdapterInterface::INDEX_TYPE_UNIQUE,
                    'nullable'  => false,
                ]
            )
            ->addIndex(
                'increment_order_id',
                ['increment_order_id'],
                [
                    'type'      => AdapterInterface::INDEX_TYPE_UNIQUE,
                ]
            );

        $setup->getConnection()->createTable($table);

        if (version_compare($context->getVersion(), '3.5.4', '<')) {
            $setup->getConnection()->addColumn(
                $setup->getTable(OrderLink::TABLE_NAME),
                'rzp_signature',
                [
                    'nullable' => true,
                    'type'     => Table::TYPE_TEXT,
                    'length'   => 255,
                    'comment'  => 'RZP signature'
                ]
            );

            $setup->getConnection()->addColumn(
                $setup->getTable(OrderLink::TABLE_NAME),
                'rzp_order_amount',
                [
                    'nullable' => true,
                    'type'     => Table::TYPE_INTEGER,
                    'length'   => 20,
                    'comment'  => 'RZP order amount'
                ]
            );
        }

        if (version_compare($context->getVersion(), '3.6.5', '<')) {
            $setup->getConnection()->addColumn(
                $setup->getTable(OrderLink::TABLE_NAME),
                'amount_paid',
                [
                    'nullable' => true,
                    'type'     => Table::TYPE_INTEGER,
                    'length'   => 20,
                    'comment'  => 'Actual paid amount'
                ]
            );

            $setup->getConnection()->addColumn(
                $setup->getTable(OrderLink::TABLE_NAME),
                'email',
                [
                    'nullable' => true,
                    'type'     => Table::TYPE_TEXT,
                    'length'   => 255,
                    'comment'  => 'payment email'
                ]
            );

            $setup->getConnection()->addColumn(
                $setup->getTable(OrderLink::TABLE_NAME),
                'contact',
                [
                    'nullable' => true,
                    'type'     => Table::TYPE_TEXT,
                    'length'   => 25,
                    'comment'  => 'payment contact'
                ]
            );
        }

        $setup->endSetup();
    }
}
Magento/Controller/0000755000000000000000000000000014364325176013304 5ustar  rootrootMagento/Controller/Payment/0000755000000000000000000000000014364325176014721 5ustar  rootrootMagento/Controller/Payment/Webhook.php0000644000000000000000000003020014364325176017023 0ustar  rootroot<?php

namespace Razorpay\Magento\Controller\Payment;

use Razorpay\Api\Api;
use Razorpay\Api\Errors;
use Razorpay\Magento\Model\Config;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\DataObject;
use Razorpay\Subscription\Helper\SubscriptionWebhook;

class Webhook extends \Razorpay\Magento\Controller\BaseController
{
    /**
     * @var \Magento\Checkout\Model\Session
     */
    protected $checkoutSession;

    /**
     * @var \Magento\Quote\Model\QuoteRepository
     */
    protected $quoteRepository;

    /**
     * @var \Magento\Sales\Api\Data\OrderInterface
     */
    protected $order;

    protected $api;

    protected $logger;

    protected $quoteManagement;

    protected $objectManagement;

    protected $storeManager;

    protected $customerRepository;

    protected $cache;

    /**
     * @var ManagerInterface
     */
    private $eventManager;

    const STATUS_APPROVED = 'APPROVED';

    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Magento\Razorpay\Model\CheckoutFactory $checkoutFactory
     * @param \Razorpay\Magento\Model\Config $config
     * @param \Magento\Quote\Model\QuoteRepository $quoteRepository,
     * @param \Magento\Sales\Api\Data\OrderInterface $order
     * @param \Magento\Quote\Model\QuoteManagement $quoteManagement
     * @param \Magento\Store\Model\StoreManagerInterface $storeManagement
     * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
     * @param \Magento\Framework\App\CacheInterface $cache
     * @param \Psr\Log\LoggerInterface $logger
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Razorpay\Magento\Model\CheckoutFactory $checkoutFactory,
        \Razorpay\Magento\Model\Config $config,
        \Magento\Quote\Model\QuoteRepository $quoteRepository,
        \Magento\Sales\Api\Data\OrderInterface $order,
        \Magento\Quote\Model\QuoteManagement $quoteManagement,
        \Magento\Store\Model\StoreManagerInterface $storeManagement,
        \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
        \Magento\Framework\App\CacheInterface $cache,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Psr\Log\LoggerInterface $logger
    )
    {
        parent::__construct(
            $context,
            $customerSession,
            $checkoutSession,
            $config
        );

        $keyId                 = $this->config->getConfigData(Config::KEY_PUBLIC_KEY);
        $keySecret             = $this->config->getConfigData(Config::KEY_PRIVATE_KEY);

        $this->api             = new Api($keyId, $keySecret);
        $this->order           = $order;
        $this->logger          = $logger;

        $this->objectManagement   = \Magento\Framework\App\ObjectManager::getInstance();
        $this->quoteManagement    = $quoteManagement;
        $this->checkoutFactory    = $checkoutFactory;
        $this->quoteRepository    = $quoteRepository;
        $this->storeManagement    = $storeManagement;
        $this->customerRepository = $customerRepository;
        $this->eventManager       = $eventManager;
        $this->cache = $cache;
    }

    /**
     * Processes the incoming webhook
     */
    public function execute()
    {
        $post = $this->getPostData();
     
        if (json_last_error() !== 0)
        {
            return;
        }

        if (($this->config->isWebhookEnabled() === true) &&
            (empty($post['event']) === false))
        {
            if (isset($_SERVER['HTTP_X_RAZORPAY_SIGNATURE']) === true)
            {
                $webhookSecret = $this->config->getWebhookSecret();

                //
                // To accept webhooks, the merchant must configure
                // it on the magento backend by setting the secret
                //
                if (empty($webhookSecret) === true)
                {
                    return;
                }

                try
                {
                    $postData = file_get_contents('php://input');

                    $this->rzp->utility->verifyWebhookSignature($postData, $_SERVER['HTTP_X_RAZORPAY_SIGNATURE'], $webhookSecret);
                }
                catch (Errors\SignatureVerificationError $e)
                {
                    $this->logger->warning(
                        $e->getMessage(),
                        [
                            'data'  => $post,
                            'event' => 'razorpay.magento.signature.verify_failed'
                        ]);

                    //Set the validation error in response
                    header('Status: 400 Signature Verification failed', true, 400);
                    exit;
                }

                switch ($post['event'])
                {
                    case 'payment.authorized':
                    case 'order.paid':
                        return $this->orderPaid($post);

                    case 'subscription.charged':
                        if(class_exists(SubscriptionWebhook::class)) {
                            $subscriptionWebhook = new SubscriptionWebhook($this->logger);
                            return $subscriptionWebhook->processSubscriptionCharged($post);
                        }
                    
                    case 'subscription.paused':
                        if(class_exists(SubscriptionWebhook::class)) {
                            $subscriptionWebhook = new SubscriptionWebhook($this->logger);
                            return $subscriptionWebhook->processSubscriptionAction($post);
                        }
                    
                    case 'subscription.resumed':
                        if(class_exists(SubscriptionWebhook::class)) {
                            $subscriptionWebhook = new SubscriptionWebhook($this->logger);
                            return $subscriptionWebhook->processSubscriptionAction($post);
                        }
                        
                    case 'subscription.cancelled':
                        if(class_exists(SubscriptionWebhook::class)) {
                            $subscriptionWebhook = new SubscriptionWebhook($this->logger);
                            return $subscriptionWebhook->processSubscriptionAction($post);
                        }     

                    default:
                        return;
                }
            }
        }

        $this->logger->info("Razorpay Webhook processing completed.");
    }

    /**
     * Order Paid webhook
     *
     * @param array $post
     */
    protected function orderPaid(array $post)
    {
        // Do not process if order is subscription type
        if (isset($post['payload']['payment']['entity']['invoice_id']) === true) {
            $rzpInvoiceId = $post['payload']['payment']['entity']['invoice_id'];
            $invoice = $this->rzp->invoice->fetch($rzpInvoiceId);
            if(isset($invoice->subscription_id)){
                $this->logger->info("Razorpay Webhook: Order is a subscription type, hence exiting from webhook since it is handled separately");
                return;
            }
        }

        $this->logger->info("Razorpay Webhook Event(" . $post['event'] . ")  processing Started.");

        $paymentId  = $post['payload']['payment']['entity']['id'];
        $rzpOrderId = $post['payload']['payment']['entity']['order_id'];

        try
        {
            if($post['event'] === 'payment.authorized')
            {
                $rzpOrder = $this->getRzpOrder($rzpOrderId);

                $quoteId = $rzpOrder->receipt;

                $rzpOrderAmount = $rzpOrder->amount;

                $amountPaid     = $post['payload']['payment']['entity']['amount'];
            }
            else
            {
                $amountPaid     = $post['payload']['order']['entity']['amount_paid'];

                $rzpOrderAmount = $post['payload']['order']['entity']['amount'];

                $quoteId   = $post['payload']['order']['entity']['receipt'];
            }

            if (isset($quoteId) === false)
            {
                $this->logger->info("Razorpay Webhook: Quote ID not set for Razorpay payment_id(:$paymentId)");
                return;
            }

            $email   = $post['payload']['payment']['entity']['email'];
            $contact = $post['payload']['payment']['entity']['contact'];
        }
        catch(\Razorpay\Api\Errors\Error $e)
        {
            $this->logger->critical("Razorpay Webhook: fetching RZP order data(id:$rzpOrderId) PaymentId:(:paymentId) failed with error: ". $e->getMessage());
            return;
        }
        catch(\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook: fetching RZP order data(id:$rzpOrderId) PaymentId:(:paymentId) failed with error: ". $e->getMessage());
            return;
        }

        try
        {
            $orderLinkCollection = $this->_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                       ->getCollection()
                                                       ->addFilter('quote_id', $quoteId)
                                                       ->addFilter('rzp_order_id', $rzpOrderId)
                                                       ->getFirstItem();

            $orderLink = $orderLinkCollection->getData();

            if (empty($orderLink['entity_id']) === false)
            {
                if ($orderLink['order_placed'])
                {
                     $this->logger->info(__("Razorpay Webhook: Quote order is inactive for quoteID: $quoteId and Razorpay payment_id(:$paymentId) with Maze OrderID (:%1) ", $orderLink['increment_order_id']));

                    return;
                }

                //set the 1st webhook notification time
                if ($orderLink['webhook_count'] < 1)
                {
                    $orderLinkCollection->setWebhookFirstNotifiedAt(time());
                }

                $paymentSignature = hash_hmac('sha256', $rzpOrderId . "|" . $paymentId, $this->config->getConfigData(Config::KEY_PRIVATE_KEY));

                $orderLinkCollection->setWebhookCount($orderLink['webhook_count'] + 1)
                                    ->setRzpPaymentId($paymentId)
                                    ->setAmountPaid($amountPaid)
                                    ->setRzpSignature($paymentSignature)
                                    ->setEmail($email)
                                    ->setContact($contact)
                                    ->save();

                return;
            }

            $this->logger->info("Razorpay Webhook Event(" . $post['event'] . ") Processed successfully for Razorpay payment_id(:$paymentId): and quoteID(: $quoteId) .");
            return;
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook: Quote submitted for order creation with quoteID:$quoteId failed with error: ". $e->getMessage());
            return;
        }
   }

    /**
     * @return Webhook post data as an array
     */
    protected function getPostData() : array
    {
        $request = file_get_contents('php://input');

        return json_decode($request, true);
    }

    /**
     * Get the Order from RZP
     *
     * @param string $orderId
     */
    public function getRzpOrder($orderId)
    {
        try
        {
            $order = $this->api->order->fetch($orderId);

            return $order;
        }
        catch(\Razorpay\Api\Errors\Error $e)
        {
            $this->logger->critical("Razorpay Webhook: fetching RZP order data(id:$orderIdorderId) failed with error: ". $e->getMessage());
            return;
        }
        catch(\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook: fetching RZP order data(id:$orderId) failed with error: ". $e->getMessage());
            return;
        }
    }
}Magento/Controller/Payment/Order.php0000644000000000000000000003222714364325176016513 0ustar  rootroot<?php

namespace Razorpay\Magento\Controller\Payment;

use Razorpay\Api\Api;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\Controller\ResultFactory;

class Order extends \Razorpay\Magento\Controller\BaseController
{
    protected $quote;

    protected $checkoutSession;

    protected $cartManagement;

    protected $cache;

    protected $orderRepository;

    protected $logger;
    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Magento\Razorpay\Model\Config\Payment $razorpayConfig
     * @param \Magento\Framework\App\CacheInterface $cache
     * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
     * @param \Psr\Log\LoggerInterface $logger
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Razorpay\Magento\Model\Config $config,
        \Magento\Quote\Api\CartManagementInterface $cartManagement,
        \Razorpay\Magento\Model\CheckoutFactory $checkoutFactory,
        \Magento\Framework\App\CacheInterface $cache,
        \Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
        \Psr\Log\LoggerInterface $logger
    ) {
        parent::__construct(
            $context,
            $customerSession,
            $checkoutSession,
            $config
        );

        $this->config          = $config;
        $this->cartManagement  = $cartManagement;
        $this->customerSession = $customerSession;
        $this->checkoutFactory = $checkoutFactory;
        $this->cache = $cache;
        $this->orderRepository = $orderRepository;
        $this->logger          = $logger;

        $this->objectManagement   = \Magento\Framework\App\ObjectManager::getInstance();
    }

    public function execute()
    {
        $receipt_id = $this->getQuote()->getId();

        if(empty($_POST['error']) === false)
        {
            $this->messageManager->addError(__('Payment Failed'));
            return $this->_redirect('checkout/cart');
        }

        if (isset($_POST['order_check']))
        {
            if (empty($this->cache->load("quote_processing_".$receipt_id)) === false)
            {
                $responseContent = [
                'success'   => true,
                'order_id'  => false,
                'parameters' => []
                ];

                # fetch the related sales order and verify the payment ID with rzp payment id
                # To avoid duplicate order entry for same quote
                $collection = $this->_objectManager->get('Magento\Sales\Model\Order')
                                                   ->getCollection()
                                                   ->addFieldToSelect('entity_id')
                                                   ->addFilter('quote_id', $receipt_id)
                                                   ->getFirstItem();

                $salesOrder = $collection->getData();

                if (empty($salesOrder['entity_id']) === false)
                {
                    $this->logger->info("Razorpay inside order already processed with webhook quoteID:" . $receipt_id
                                    ." and OrderID:".$salesOrder['entity_id']);

                    $this->checkoutSession
                            ->setLastQuoteId($this->getQuote()->getId())
                            ->setLastSuccessQuoteId($this->getQuote()->getId())
                            ->clearHelperData();

                    $order = $this->orderRepository->get($salesOrder['entity_id']);

                    if ($order) {
                        $this->checkoutSession->setLastOrderId($order->getId())
                                           ->setLastRealOrderId($order->getIncrementId())
                                           ->setLastOrderStatus($order->getStatus());
                    }

                    $responseContent['order_id'] = true;
                }
            }
            else
            {
                if(empty($receipt_id) === false)
                {
                    //set the chache to stop webhook processing
                    $this->cache->save("started", "quote_Front_processing_$receipt_id", ["razorpay"], 30);

                    $this->logger->info("Razorpay front-end order processing started quoteID:" . $receipt_id);

                    $responseContent = [
                    'success'   => false,
                    'parameters' => []
                    ];
                }
                else
                {
                    $this->logger->info("Razorpay order already processed with quoteID:" . $this->checkoutSession
                            ->getLastQuoteId());

                    $responseContent = [
                        'success'    => true,
                        'order_id'   => true,
                        'parameters' => []
                    ];

                }
            }

            $response = $this->resultFactory->create(ResultFactory::TYPE_JSON);
            $response->setData($responseContent);
            $response->setHttpResponseCode(200);

            return $response;
        }

        //validate shipping and billing
        $validationSuccess =  true;
        $code = 200;

        if(empty($_POST['email']) === true)
        {
            $this->logger->info("Email field is required");

            $responseContent = [
                'message'   => "Email field is required",
                'parameters' => []
            ];

            $validationSuccess = false;
        }

        if(empty($this->getQuote()->getBillingAddress()->getPostcode()) === true)
        {
            $responseContent = [
                'message'   => "Billing Address is required",
                'parameters' => []
            ];

            $validationSuccess = false;
        }

        if(!$this->getQuote()->getIsVirtual())
        {
             //validate quote Shipping method
            if(empty($this->getQuote()->getShippingAddress()->getShippingMethod()) === true)
            {
                $responseContent = [
                    'message'   => "Shipping method is required",
                    'parameters' => []
                ];

                $validationSuccess = false;
            }

            if(empty($this->getQuote()->getShippingAddress()->getPostcode()) === true)
            {
                $responseContent = [
                    'message'   => "Shipping Address is required",
                    'parameters' => []
                ];

                $validationSuccess = false;
            }
        }

        if($validationSuccess)
        {
            $amount = (int) (number_format($this->getQuote()->getGrandTotal() * 100, 0, ".", ""));

            $payment_action = $this->config->getPaymentAction();

            $maze_version = $this->_objectManager->get('Magento\Framework\App\ProductMetadataInterface')->getVersion();
            $module_version =  $this->_objectManager->get('Magento\Framework\Module\ModuleList')->getOne('Razorpay_Magento')['setup_version'];

            $this->customerSession->setCustomerEmailAddress($_POST['email']);

            if ($payment_action === 'authorize')
            {
                $payment_capture = 0;
            }
            else
            {
                $payment_capture = 1;
            }

            $code = 400;

            try
            {
                //save to razorpay orderLink
                $orderLinkCollection = $this->_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                       ->getCollection()
                                                       ->addFilter('quote_id', $receipt_id)
                                                       ->getFirstItem();

                $orderLinkData = $orderLinkCollection->getData();

                $createNewOrder = true;

                if (empty($orderLinkData['entity_id']) === false)
                {
                    if (((int) $orderLinkData['rzp_order_amount'] === $amount)  and (isset($orderLinkData['rzp_order_id']) === true))
                    {
                        $createNewOrder = false;

                    }
                }

                if ($createNewOrder)
                {
                    $order = $this->rzp->order->create([
                        'amount' => $amount,
                        'receipt' => $receipt_id,
                        'currency' => $this->getQuote()->getQuoteCurrencyCode(),
                        'payment_capture' => $payment_capture,
                        'app_offer' => ($this->getDiscount() > 0) ? 1 : 0
                    ]);
                }
                else
                {
                    if (isset($orderLinkData['rzp_order_id']) === true)
                    {
                        $order = $this->rzp->order->fetch($orderLinkData['rzp_order_id']);
                    }
                }

                $responseContent = [
                    'message'   => 'Unable to create your order. Please contact support.',
                    'parameters' => []
                ];

                if (null !== $order && !empty($order->id))
                {
                    $is_hosted = false;

                    $merchantPreferences    = $this->getMerchantPreferences();

                    $responseContent = [
                        'success'           => true,
                        'rzp_order'         => $order->id,
                        'order_id'          => $receipt_id,
                        'amount'            => $order->amount,
                        'quote_currency'    => $this->getQuote()->getQuoteCurrencyCode(),
                        'quote_amount'      => number_format($this->getQuote()->getGrandTotal(), 2, ".", ""),
                        'maze_version'      => $maze_version,
                        'module_version'    => $module_version,
                        'is_hosted'         => $merchantPreferences['is_hosted'],
                        'image'             => $merchantPreferences['image'],
                        'embedded_url'      => $merchantPreferences['embedded_url'],
                    ];

                    $code = 200;

                    $this->checkoutSession->setRazorpayOrderID($order->id);
                    $this->checkoutSession->setRazorpayOrderAmount($amount);

                    if (empty($orderLinkData['entity_id']) === false)
                    {
                        $orderLinkCollection->setRzpOrderId($order->id)
                                            ->setRzpOrderAmount($amount)
                                            ->setEmail($_POST['email'])
                                            ->save();
                    }
                    else
                    {
                        $orderLnik = $this->_objectManager->create('Razorpay\Magento\Model\OrderLink');
                        $orderLnik->setQuoteId($receipt_id)
                                  ->setRzpOrderId($order->id)
                                  ->setRzpOrderAmount($amount)
                                  ->setEmail($_POST['email'])
                                  ->save();
                    }

                }
            }
            catch(\Razorpay\Api\Errors\Error $e)
            {
                $responseContent = [
                    'message'   => $e->getMessage(),
                    'parameters' => []
                ];
            }
            catch(\Exception $e)
            {
                $responseContent = [
                    'message'   => $e->getMessage(),
                    'parameters' => []
                ];
            }
        }

        //set the chache for race with webhook
        $this->cache->save("started", "quote_Front_processing_$receipt_id", ["razorpay"], 300);

        $response = $this->resultFactory->create(ResultFactory::TYPE_JSON);
        $response->setData($responseContent);
        $response->setHttpResponseCode($code);

        return $response;

    }

    public function getOrderID()
    {
        return $this->checkoutSession->getRazorpayOrderID();
    }

    public function getRazorpayOrderAmount()
    {
        return $this->checkoutSession->getRazorpayOrderAmount();
    }

    protected function getMerchantPreferences()
    {
        try
        {
            $api = new Api($this->config->getKeyId(),"");

            $response = $api->request->request("GET", "preferences");
        }
        catch (\Razorpay\Api\Errors\Error $e)
        {
            echo 'Magento Error : ' . $e->getMessage();
        }

        $preferences = [];

        $preferences['embedded_url'] = Api::getFullUrl("checkout/embedded");
        $preferences['is_hosted'] = false;
        $preferences['image'] = $response['options']['image'];

        if(isset($response['options']['redirect']) && $response['options']['redirect'] === true)
        {
            $preferences['is_hosted'] = true;
        }

        return $preferences;
    }

    public function getDiscount()
    {
        return ($this->getQuote()->getBaseSubtotal() - $this->getQuote()->getBaseSubtotalWithDiscount());
    }
}
Magento/Controller/Payment/WebhookOrderCron.php0000644000000000000000000003473314364325176020660 0ustar  rootroot<?php 

namespace Razorpay\Magento\Controller\Payment;

use Razorpay\Api\Api;
use Razorpay\Api\Errors;
use Razorpay\Magento\Model\Config;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\DataObject;

class WebhookOrderCron extends \Razorpay\Magento\Controller\BaseController
{
    /**
     * @var \Magento\Checkout\Model\Session
     */
    protected $checkoutSession;

    /**
     * @var \Magento\Quote\Model\QuoteRepository
     */
    protected $quoteRepository;

    /**
     * @var \Magento\Sales\Api\Data\OrderInterface
     */
    protected $order;

    protected $api;

    protected $logger;

    protected $quoteManagement;

    protected $objectManagement;

    protected $storeManager;

    protected $customerRepository;

    protected $cache;

    /**
     * @var ManagerInterface
     */
    private $eventManager;

    const STATUS_APPROVED = 'APPROVED';

    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Magento\Razorpay\Model\CheckoutFactory $checkoutFactory
     * @param \Razorpay\Magento\Model\Config $config
     * @param \Magento\Quote\Model\QuoteRepository $quoteRepository,
     * @param \Magento\Sales\Api\Data\OrderInterface $order
     * @param \Magento\Quote\Model\QuoteManagement $quoteManagement
     * @param \Magento\Store\Model\StoreManagerInterface $storeManagement
     * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
     * @param \Magento\Framework\App\CacheInterface $cache
     * @param \Psr\Log\LoggerInterface $logger
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Razorpay\Magento\Model\CheckoutFactory $checkoutFactory,
        \Razorpay\Magento\Model\Config $config,
        \Magento\Quote\Model\QuoteRepository $quoteRepository,
        \Magento\Sales\Api\Data\OrderInterface $order,
        \Magento\Quote\Model\QuoteManagement $quoteManagement,
        \Magento\Store\Model\StoreManagerInterface $storeManagement,
        \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
        \Magento\Framework\App\CacheInterface $cache,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Psr\Log\LoggerInterface $logger
    ) 
    {
        parent::__construct(
            $context,
            $customerSession,
            $checkoutSession,
            $config
        );

        $keyId                 = $this->config->getConfigData(Config::KEY_PUBLIC_KEY);
        $keySecret             = $this->config->getConfigData(Config::KEY_PRIVATE_KEY);

        $this->api             = new Api($keyId, $keySecret);
        $this->order           = $order;
        $this->logger          = $logger;

        $this->objectManagement   = \Magento\Framework\App\ObjectManager::getInstance();
        $this->quoteManagement    = $quoteManagement;
        $this->checkoutFactory    = $checkoutFactory;
        $this->quoteRepository    = $quoteRepository;
        $this->storeManagement    = $storeManagement;
        $this->customerRepository = $customerRepository;
        $this->eventManager       = $eventManager;
        $this->cache = $cache;
    }

    /**
     * Processes the incoming webhook
     */
    public function execute()
    {       
        $this->logger->info("Razorpay Webhook Order Cron job processing started.");
        try
        {

            $orderLinkCollection = $this->_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                        ->getCollection()
                                                        ->addFilter('order_placed', 0)
                                                        ->addFieldToFilter('webhook_count',["lt" => 4])
                                                        ->addFieldToFilter('webhook_first_notified_at',["notnull" => true])
                                                        ->addFieldToFilter('amount_paid',["notnull" => true])
                                                        ->setOrder('webhook_first_notified_at')
                                                        ->setPageSize(5);

            $orderLink = $orderLinkCollection->getData();

            if(count($orderLink) > 0)
            {
                foreach ($orderLink as $orderData)
                {
                     $this->createOrder($orderData);  
                }
            }
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook Order Cron: Quote submitted for order creation failed with error: ". $e->getMessage());
            return;
        }
        $this->logger->info("Razorpay Webhook Order Cron job completed.");
    }

    /**
     * Order Paid create
     * 
     * @param array $orderData
     */
    protected function createOrder($orderData = array())
    {

        $quoteId    = $orderData['quote_id'];
        $entityId   = $orderData['entity_id'];

        try
        {
            
            $orderLinkCollection = $this->_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                        ->getCollection()
                                                        ->addFilter('entity_id', $entityId)
                                                        ->getFirstItem();
            $orderLink = $orderLinkCollection->getData();
            
            $paymentId = $orderLink['rzp_payment_id'];

            $webhookWaitTime = $this->config->getConfigData(Config::WEBHOOK_WAIT_TIME) ? $this->config->getConfigData(Config::WEBHOOK_WAIT_TIME) : 300;

            //ignore webhook call for some time as per config, from first webhook call
            if ((time() - $orderLinkCollection->getWebhookFirstNotifiedAt()) < $webhookWaitTime)
            {
                $this->logger->info(__("Razorpay Webhook Cron: Order processing is active for quoteID: $quoteId and Razorpay payment_id(:$paymentId) and webhook attempt: %1", ($orderLink['webhook_count'] + 1)));
                
                return;
            }

            $orderLinkCollection->setWebhookCount($orderLink['webhook_count'] + 1)
                                ->save();

            //validate if the quote Order is still active
            $quote = $this->quoteRepository->get($quoteId);

            //exit if quote is not active
            if (!$quote->getIsActive())
            {
                $this->logger->info("Razorpay Webhook Cron: Quote order is inactive for quoteID: $quoteId and Razorpay payment_id(:$paymentId)");

                return;
            }

            //validate amount before placing order
           $quoteAmount    = (int) (number_format($quote->getGrandTotal() * 100, 0, ".", ""));
           $rzpOrderAmount = (int) (number_format($orderLink['rzp_order_amount'], 0, ".", ""));

            if ($quoteAmount !== $rzpOrderAmount)
            {
                $this->logger->critical("Razorpay Webhook Cron: Amount processed for payment doesn't match with store order amount for Razorpay payment_id(:$paymentId) and quote (:$quoteId)");

                if ($this->config->isSkipOrderEnabled() === true)
                {
                    return;
                }
            }

            # fetch the related sales order and verify the payment ID with rzp payment id
            # To avoid duplicate order entry for same quote
            $collection = $this->_objectManager->get('Magento\Sales\Model\Order')
                                               ->getCollection()
                                               ->addFieldToSelect('entity_id')
                                               ->addFilter('quote_id', $quoteId)
                                               ->getFirstItem();

            $salesOrder = $collection->getData();

            if (empty($salesOrder['entity_id']) === false)
            {
                $order = $this->order->load($salesOrder['entity_id']);
                $orderRzpPaymentId = $order->getPayment()->getLastTransId();

                if ($orderRzpPaymentId === $paymentId)
                {
                    $this->logger->info("Razorpay Webhook Cron: Sales Order and payment already exist for Razorpay payment_id(:$paymentId)");

                    return;
                }
            }

            $quote = $this->getQuoteObject($orderLink, $quoteId);

            $this->logger->info("Razorpay Webhook Cron: Order creation started with quoteID:$quoteId.");

            //validate if the quote Order is still active
            $quoteUpdated = $this->quoteRepository->get($quoteId);

            //exit if quote is not active
            if (!$quoteUpdated->getIsActive())
            {
                $this->logger->info("Razorpay Webhook Cron: Quote order is inactive for quoteID: $quoteId and Razorpay payment_id(:$paymentId)");

                return;
            }

            //verify Rzp OrderLink status
            $orderLinkCollection = $this->_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                       ->getCollection()
                                                       ->addFilter('entity_id', $entityId)
                                                       ->getFirstItem();

            $orderLink = $orderLinkCollection->getData();

            if (empty($orderLink['entity_id']) === false)
            {
                if ($orderLink['order_placed'])
                {
                    $this->logger->info(__("Razorpay Webhook Cron: Quote order is inactive for quoteID: $quoteId and Razorpay payment_id(:$paymentId) with Maze OrderID (:%1) ", $orderLink['increment_order_id']));

                    return;
                }

                $amount = $orderLink['amount_paid'];
            }
           

            $this->logger->info("Razorpay Webhook Cron: Quote submitted for order creation with quoteID:$quoteId.");

            $order = $this->quoteManagement->submit($quote);

            $payment = $order->getPayment();

            $this->logger->info("Razorpay Webhook Cron: Adding payment to order for quoteID:$quoteId.");

            $payment->setAmountPaid($amount)
                    ->setLastTransId($paymentId)
                    ->setTransactionId($paymentId)
                    ->setIsTransactionClosed(true)
                    ->setShouldCloseParentTransaction(true);

            //set razorpay webhook fields
            $order->setByRazorpayWebhook(1);

            $order->save();

            //disable the quote
            $quote->setIsActive(0)->save();

            //dispatch the "razorpay_webhook_order_placed_after" event
            $eventData = [
                            'raorpay_payment_id' => $paymentId,
                            'magento_quote_id' => $quoteId,
                            'magento_order_id' => $order->getEntityId(),
                            'amount_captured' => $amount
                         ];

            $transport = new DataObject($eventData);

            $this->eventManager->dispatch(
                'razorpay_webhook_order_placed_after',
                [
                    'context'   => 'razorpay_webhook_order',
                    'payment'   => $paymentId,
                    'transport' => $transport
                ]
            );

            $this->logger->info("Razorpay Webhook Cron Processed successfully for Razorpay payment_id(:$paymentId): and quoteID(: $quoteId) and OrderID(: ". $order->getEntityId() .")");
            return;
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook Cron: Quote submitted for order creation with quoteID:$quoteId failed with error: ". $e->getMessage());
            return;
        }
   }

    protected function getQuoteObject($post, $quoteId)
    {
        try
        {
            $quote = $this->quoteRepository->get($quoteId);

            $firstName = $quote->getBillingAddress()->getFirstname() ?? 'null';
            $lastName  = $quote->getBillingAddress()->getLastname() ?? 'null';
            $email     = $quote->getBillingAddress()->getEmail() ?? $post['email'];

            $quote->getPayment()->setMethod(PaymentMethod::METHOD_CODE);

            $store = $quote->getStore();

            if(empty($store) === true)
            {
                $store = $this->storeManagement->getStore();
            }

            $websiteId = $store->getWebsiteId();

            $customer = $this->objectManagement->create('Magento\Customer\Model\Customer');

            $customer->setWebsiteId($websiteId);

            //get customer from quote , otherwise from payment email
            $customer = $customer->loadByEmail($email);

            //if quote billing address doesn't contains address, set it as customer default billing address
            if ((empty($quote->getBillingAddress()->getFirstname()) === true) and
                (empty($customer->getEntityId()) === false))
            {
                $quote->getBillingAddress()->setCustomerAddressId($customer->getDefaultBillingAddress()['id']);
            }

            //If need to insert new customer as guest
            if ((empty($customer->getEntityId()) === true) or
                (empty($quote->getBillingAddress()->getCustomerId()) === true))
            {
                $quote->setCustomerFirstname($firstName);
                $quote->setCustomerLastname($lastName);
                $quote->setCustomerEmail($email);
                $quote->setCustomerIsGuest(true);
            }

            //skip address validation as some time billing/shipping address not set for the quote
            $quote->getBillingAddress()->setShouldIgnoreValidation(true);
            $quote->getShippingAddress()->setShouldIgnoreValidation(true);

            $quote->setStore($store);

            $quote->collectTotals();

            $quote->save();

            return $quote;
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay Webhook Cron: Unable to update/get quote with quoteID:$quoteId, failed with error: ". $e->getMessage());
            return;
        }
    }

    /**
     * @return Webhook post data as an array
     */
    protected function getPostData() : array
    {
        $request = file_get_contents('php://input');

        return json_decode($request, true);
    }
}
Magento/Controller/Payment/Callback.php0000644000000000000000000002160014364325176017125 0ustar  rootroot<?php

namespace Razorpay\Magento\Controller\Payment;

use Razorpay\Api\Api;
use Razorpay\Magento\Model\PaymentMethod;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\App\Action\Context;
use Magento\Framework\App\RequestInterface;

class Callback extends \Razorpay\Magento\Controller\BaseController
{
    protected $quote;

    protected $checkoutSession;

    protected $cartManagement;

    protected $cache;

    protected $orderRepository;

    protected $logger;
    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Magento\Razorpay\Model\Config\Payment $razorpayConfig
     * @param \Magento\Framework\App\CacheInterface $cache
     * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
     * @param \Psr\Log\LoggerInterface $logger
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Razorpay\Magento\Model\Config $config,
        \Magento\Quote\Api\CartManagementInterface $cartManagement,
        \Razorpay\Magento\Model\CheckoutFactory $checkoutFactory,
        \Magento\Framework\App\CacheInterface $cache,
        \Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
        \Magento\Quote\Model\QuoteRepository $quoteRepository,
        \Magento\Quote\Model\QuoteManagement $quoteManagement,
        \Magento\Customer\Model\CustomerFactory $customerFactory,
        \Psr\Log\LoggerInterface $logger
    ) {
        parent::__construct(
            $context,
            $customerSession,
            $checkoutSession,
            $config
        );

        $this->config          = $config;
        $this->cartManagement  = $cartManagement;
        $this->customerSession = $customerSession;
        $this->checkoutFactory = $checkoutFactory;
        $this->orderRepository = $orderRepository;
        $this->quoteRepository = $quoteRepository;
        $this->quoteManagement = $quoteManagement;
        $this->customerFactory = $customerFactory;
        $this->logger          = $logger;

        $this->objectManagement   = \Magento\Framework\App\ObjectManager::getInstance();
    }

    public function execute()
    {
        $params = $this->getRequest()->getParams();
                
        $quoteId = strip_tags($params["order_id"]);

        if(empty($quoteId) === true)
        {
            $this->messageManager->addError(__('Razorpay front-end callback: Payment Failed, As no active cart ID found.'));

            return $this->_redirect('checkout/cart');
        }
        
        $quote = $this->getQuoteObject($params, $quoteId);

        if(!$this->customerSession->isLoggedIn())
        {
            $customerId = $quote->getCustomer()->getId();

            if(!empty($customerId))
            {
                $customer = $this->customerFactory->create()->load($customerId);

                $this->customerSession->setCustomerAsLoggedIn($customer);
            }
        }

        if(isset($params['razorpay_payment_id']))
        {
            if(isset($quoteId) and
               (empty($quoteId) === false))
            {
                try
                {
                    $this->logger->info('Razorpay front-end callback: for cartId- ' . $quoteId);

                    $quote->getPayment()->setMethod(PaymentMethod::METHOD_CODE);

                    if(!$this->customerSession->isLoggedIn())
                    {
                        $quote->setCheckoutMethod($this->cartManagement::METHOD_GUEST);
                    }

                    if($quote->getIsActive())
                    {
                        $order = $this->quoteManagement->submit($quote);

                        $this->logger->info(__('Razorpay front-end callback: order Id- ' . $order->getId()));

                        $this->checkoutSession->setLastSuccessQuoteId($quote->getId())
                                              ->setLastQuoteId($quote->getId())
                                              ->clearHelperData();

                        if(empty($order) === false)
                        {
                            $quote->setIsActive(false)->save();

                            $this->checkoutSession->replaceQuote($quote);

                            $this->checkoutSession->setLastOrderId($order->getId())
                                                  ->setLastRealOrderId($order->getIncrementId())
                                                  ->setLastOrderStatus($order->getStatus());
                        }
                    }
                    else
                    {
                        $this->logger->info("Razorpay front-end callback: Quote order is inactive for quoteID: $quoteId");
                    }

                    return $this->_redirect('checkout/onepage/success');

                    exit;
                }
                catch(\Exception $e)
                {
                    $quote->setIsActive(1)->setReservedOrderId(null)->save();

                    $this->logger->critical(__('Razorpay front-end callback: ' . $e->getMessage()));
                    
                    $this->messageManager->addError(__($e->getMessage()));

                    return $this->_redirect('checkout/cart');
                }
            }
            else
            {
                $this->logger->critical(__('Razorpay front-end callback: Quote ID missing on callback from RZP.' ));
            }
        }
        else
        {
            $quote->setIsActive(1)->setReservedOrderId(null)->save();

            $this->checkoutSession->replaceQuote($quote);

            $this->logger->critical(__('Razorpay front-end callback: Payment Failed with response:  ' . json_encode($params, 1) ));
            
            $this->messageManager->addError(__('Payment Failed.'));

            return $this->_redirect('checkout/cart');
        }

    }

    protected function getQuoteObject($post, $quoteId)
    {
        try
        {
            $quote = $this->quoteRepository->get($quoteId);

            $firstName = $quote->getBillingAddress()->getFirstname() ?? 'null';
            $lastName  = $quote->getBillingAddress()->getLastname() ?? 'null';
            $email     = $quote->getBillingAddress()->getEmail() ?? 'null';

            $quote->getPayment()->setMethod(PaymentMethod::METHOD_CODE);

            $store = $quote->getStore();

            if(empty($store) === true)
            {
                $store = $this->storeManagement->getStore();
            }

            $websiteId = $store->getWebsiteId();

            $customer = $this->objectManagement->create('Magento\Customer\Model\Customer');

            $customer->setWebsiteId($websiteId);

            $orderLinkCollection = $this->_objectManager->get('Razorpay\Magento\Model\OrderLink')
                                                       ->getCollection()
                                                       ->addFilter('quote_id', $quote->getId())
                                                       ->getFirstItem();

            $orderLinkData = $orderLinkCollection->getData();

            if (empty($orderLinkData['entity_id']) === false)
            {
                $email = $orderLinkData['email'] ?? $email;
            }

            //get customer from quote , otherwise from payment email
            $customer = $customer->loadByEmail($email);

            //if quote billing address doesn't contains address, set it as customer default billing address
            if ((empty($quote->getBillingAddress()->getFirstname()) === true) and
                (empty($customer->getEntityId()) === false))
            {
                $quote->getBillingAddress()->setCustomerAddressId($customer->getDefaultBillingAddress()['id']);
            }

            //If need to insert new customer as guest
            if ((empty($customer->getEntityId()) === true) or
                (empty($quote->getBillingAddress()->getCustomerId()) === true))
            {
                $quote->setCustomerFirstname($firstName);
                $quote->setCustomerLastname($lastName);
                $quote->setCustomerEmail($email);
                $quote->setCustomerIsGuest(true);
            }

            //skip address validation as some time billing/shipping address not set for the quote
            $quote->getBillingAddress()->setShouldIgnoreValidation(true);
            $quote->getShippingAddress()->setShouldIgnoreValidation(true);

            $quote->setStore($store);

            $quote->collectTotals();

            $quote->save();

            return $quote;
        }
        catch (\Exception $e)
        {
            $this->logger->critical("Razorpay front-end callback: Unable to update/get quote with quoteID:$quoteId, failed with error: ". $e->getMessage());
            return;
        }
    }

}
Magento/Controller/BaseController.php0000644000000000000000000000561014364325176016735 0ustar  rootroot<?php

namespace Razorpay\Magento\Controller;

// require in case of zip installation without composer
require_once __DIR__ . "/../../Razorpay/Razorpay.php";

use Razorpay\Api\Api;
use Razorpay\Magento\Model\Config;
use Magento\Framework\App\RequestInterface;

/**
 * Razorpay Base Controller
 */
abstract class BaseController extends \Magento\Framework\App\Action\Action
{
    /**
     * @var \Razorpay\Magento\Model\CheckoutFactory
     */
    protected $checkoutFactory;

    /**
     * @var \Magento\Quote\Model\Quote
     */
    protected $quote = false;

    /**
     * @var \Magento\Customer\Model\Session
     */
    protected $customerSession;

    /**
     * @var \Magento\Checkout\Model\Session
     */
    protected $checkoutSession;

    /**
     * @var \Razorpay\Magento\Model\Checkout
     */
    protected $checkout;

    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Razorpay\Magento\Model\Config $config
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Razorpay\Magento\Model\Config $config
    ) {
        parent::__construct($context);
        $this->customerSession = $customerSession;
        $this->checkoutSession = $checkoutSession;
        $this->config = $config;

        $this->key_id = $this->config->getConfigData(Config::KEY_PUBLIC_KEY);
        $this->key_secret = $this->config->getConfigData(Config::KEY_PRIVATE_KEY);

        $this->rzp = new Api($this->key_id, $this->key_secret);
    }

    /**
     * Instantiate quote and checkout
     *
     * @return void
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    protected function initCheckout()
    {
        $quote = $this->getQuote();
        if (!$quote->hasItems() || $quote->getHasError()) {
            $this->getResponse()->setStatusHeader(403, '1.1', 'Forbidden');
            throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t initialize checkout.'));
        }
    }

    /**
     * Return checkout quote object
     *
     * @return \Magento\Quote\Model\Quote
     */
    protected function getQuote()
    {
        if (!$this->quote) {
            $this->quote = $this->checkoutSession->getQuote();
        }
        return $this->quote;
    }

    /**
     * @return \Razorpay\Magento\Model\Checkout
     */
    protected function getCheckout()
    {
        if (!$this->checkout) {
            $this->checkout = $this->checkoutFactory->create(
                [
                    'params' => [
                        'quote' => $this->checkoutSession->getQuote(),
                    ],
                ]
            );
        }
        return $this->checkout;
    }
}Magento/CHANGELOG.md0000644000000000000000000000613414364325176012776 0ustar  rootroot
# Change Log


## [3.7.3] - 2021-01-07

### Changed
- [Added, support for subscription webhook events](https://github.com/razorpay/razorpay-magento/pull/300).
### Fixed
- [Fixed issue, Webhook gets disabled in case of mutistore site](https://github.com/razorpay/razorpay-magento/pull/301).
- [Fixed issue, Verify the active quote, before placing order in callback](https://github.com/razorpay/razorpay-magento/pull/299).

## [3.7.2] - 2021-12-06

### Changed
### Fixed
- [Fixed issue, related with new RZP order ID generation for same quote and cart amount](https://github.com/razorpay/razorpay-magento/pull/292).
- [Added, Change order status to `Payment Review` in case of amount mismatch (By using setting configs)](https://github.com/razorpay/razorpay-magento/pull/296).

## [3.7.1] - 2021-10-20

### Changed
### Fixed
- [Fixed issue, related with quote ID missing in the callback url](https://github.com/razorpay/razorpay-magento/pull/288).
- [Added webhook order cron under `razorpay` group](https://github.com/razorpay/razorpay-magento/pull/289).

## [3.7.0] - 2021-10-07

### Changed
### Fixed
- [Added Cron, to create missing webhook orders](https://github.com/razorpay/razorpay-magento/pull/284).
- [Added `payment.authorize` webhook event](https://github.com/razorpay/razorpay-magento/pull/284).

## [3.6.4] - 2021-09-20

### Changed
### Fixed
- [Fixed to validate order amount in webhook)](https://github.com/razorpay/razorpay-magento/pull/275).
- [Fixed paymentId validation](https://github.com/razorpay/razorpay-magento/pull/276).
- [Added refund through invoice credit memo](https://github.com/razorpay/razorpay-magento/pull/272).

## [3.6.3] - 2021-07-12

### Changed
### Fixed
- [Fixed to avoid api calls in observer (In case order gets updated again by other modules)](https://github.com/razorpay/razorpay-magento/pull/269).
- [Added latest release upgrade notification](https://github.com/razorpay/razorpay-magento/pull/264).
- [Additional Config support for magento GraphQL](https://github.com/razorpay/razorpay-magento/pull/268).

## [3.6.2] - 2021-06-23

### Changed
### Fixed
- [Fixed webhook localhost url validation](https://github.com/razorpay/razorpay-magento/pull/257).

 Fixed webhook localhost url validation.

## [3.6.1] - 2021-06-17
 
### Changed
### Fixed
- [Signature issue, billing address validation](https://github.com/razorpay/razorpay-magento/pull/254).

 Fixed webhook signature mismatch issue and added validation for shipping/billing address and shipping method for quote, before creating RZP order.

## [3.6.0] - 2021-06-11
  
### Added

- [GraphQL Support](https://github.com/razorpay/razorpay-magento/pull/240).

 Razorpay GraphQL Support added in this release. Please follow the readme file for instructions of uses. 
 
### Changed   
### Fixed

## [3.5.3] - 2021-06-03 

### Added

- [Actual amount paid in order comments section ( In case of Offer/Fee applied on RZP dashboard.)](https://github.com/razorpay/razorpay-magento/pull/249).
 
### Changed   

Added label for front-end to fix radio button selection issue.

### Fixed

- [Webhook signature mismatch](https://github.com/razorpay/razorpay-magento/pull/251).Magento/README.md0000644000000000000000000000662714364325176012453 0ustar  rootroot## Razorpay Payment Extension for Magento

This extension utilizes Razorpay API and provides seamless integration with Magento, allowing payments for Indian merchants via Credit Cards, Debit Cards, Net Banking, Wallets and EMI without redirecting away from the magento site.

### Installation

Install the extension through composer package manager.

```
composer require razorpay/magento
bin/magento module:enable Razorpay_Magento
```

### Install through "code.zip" file

Extract the attached code.zip from release

Go to "app" folder

Overwrite content of "code" folder with step one "code" folder (Note: if code folder not exist just place the code folder from step-1).

Run from magento root folder.

```
bin/magento module:enable Razorpay_Magento
bin/magento setup:upgrade
```

You can check if the module has been installed using `bin/magento module:status`

You should be able to see `Razorpay_Magento` in the module list


Go to `Admin -> Stores -> Configuration -> Payment Method -> Razorpay` to configure Razorpay


If you do not see Razorpay in your gateway list, please clear your Magento Cache from your admin
panel (System -> Cache Management).

### Note: Don't mix composer and zip install.

### Note: Make sure "zipcode" must be required field for billing and shipping address.**

### Setting up the cron to process missing orders

Razopray webhook cron is added under "razorpay" group, and can be run manually like below:

```
bin/magento cron:run --group="razorpay"
```
### Working with GraphQL 

Razorpay GraphQL Support added with version 3.6.0 

Order flow for placing Magento Order using Razorpay as payment method with GraphQl

1. set Payment Method on Cart
```
mutation {
  setPaymentMethodOnCart(input: {
      cart_id: "{{cart_ID}}"
      payment_method: {
          code: "razorpay"
      }
  }) {
    cart {
      selected_payment_method {
        code
      }
    }
  }
}
```

2. Create Razorpay Order ID against the cart 
```
mutation {
  placeRazorpayOrder (
    cart_id: "{{cartid}}"
  ){
    success
    rzp_order_id
    order_quote_id
    amount
    currency
    message
  }
}
```

3. Use `rzp_order_id` and other details from step-2 and create from the Frontend/React/using razorpay's checkout.js , complete the payment and obtain razorpay_payment_id & razorpay_signature
  https://razorpay.com/docs/payment-gateway/web-integration/standard/

4. Save Razorpay Response Details against Cart after payment success with RZP paymentId , orderId and signature 
```
mutation {
  setRzpPaymentDetailsOnCart (
    input: {
      cart_id: "{{cart_ID}}"
      rzp_payment_id: "{{RAZORPAY_PAYMENT_ID}}"
      rzp_order_id: "{{RAZORPAY_ORDER_ID}}"
      rzp_signature: "{{RAZORPAY_SIGNATURE}}"
    }
  ){
  cart{
    id
  }
  }
}
```
5. Finally Place Magento Order 
```
mutation {
  placeOrder(input: {cart_id: "{{cart_ID}}"}) {
    order {
      order_number
    }
  }
}
```

### Support

Visit [https://razorpay.com](https://razorpay.com) for support requests or email contact@razorpay.com.

### DISCLAIMER

In no event shall Razorpay.com/Razorpay be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from the information or code provided or the use of the information or code provided. This disclaimer of liability refers to any technical issue or damage caused by the use or non-use of the information or code provided or by the use of incorrect or incomplete information or code provided.
Magento/Plugin/0000755000000000000000000000000014364325176012417 5ustar  rootrootMagento/Plugin/CsrfValidatorSkip.php0000644000000000000000000000115514364325176016524 0ustar  rootroot<?php
namespace Razorpay\Magento\Plugin;
class CsrfValidatorSkip
{
    /**
     * @param \Magento\Framework\App\Request\CsrfValidator $subject
     * @param \Closure $proceed
     * @param \Magento\Framework\App\RequestInterface $request
     * @param \Magento\Framework\App\ActionInterface $action
     */
    public function aroundValidate(
        $subject,
        \Closure $proceed,
        $request,
        $action
    ) { 
        if ($request->getModuleName() == 'razorpay') {
            return; // Skip CSRF check
        }
        $proceed($request, $action); // Proceed Magento 2 core functionalities
    }
}