From cd6c9e7268f24d3ce35b4032fafcc2ccd1e36de9 Mon Sep 17 00:00:00 2001 From: Mikhail Yarmoluk Date: Thu, 30 May 2019 15:19:59 +0300 Subject: [PATCH 01/15] Add 3ds dialog. Upgrade package. Change android sdk version to 28. Refactor validate card number and card expire. --- README.md | 51 ++++++++++--- android/build.gradle | 8 +- android/src/main/AndroidManifest.xml | 17 +++-- .../com/rncloudpayments/CheckoutActivity.java | 69 ++++++++++++++++++ .../com/rncloudpayments/CloudPayments.java | 47 ++++++++++-- android/src/main/libs/CloudPayments.aar | Bin 86100 -> 0 bytes android/src/main/res/values/strings.xml | 2 + index.js | 20 ++++- ios/RNCloudPayments.m | 13 +++- ios/SDK/Card.h | 2 + ios/SDK/Card.m | 17 +++++ 11 files changed, 213 insertions(+), 33 deletions(-) create mode 100644 android/src/main/java/com/rncloudpayments/CheckoutActivity.java delete mode 100755 android/src/main/libs/CloudPayments.aar create mode 100644 android/src/main/res/values/strings.xml diff --git a/README.md b/README.md index c1e0ba5..e5fe202 100644 --- a/README.md +++ b/README.md @@ -20,26 +20,35 @@ react-native link react-native-cloudpayments ``` # Methods -### isValidCard() -Validate card. +### isValidNumber() +Validate card number. Returns a `Promise` that resolve card status (`Boolean`). __Arguments__ - `cardNumber` - `String` Number of payment card. -- `cardExp` - `String` Expire date of payment card. -- `cardCvv` - `String` CVV code of payment card. __Examples__ ```js import RNCloudPayment from 'react-native-cloudpayments'; -const demoCard = { - number: '5105105105105100', - extDate: '10/18', - cvvCode: '123', -}; +RNCloudPayment.isValidNumber('5105105105105100') + .then(cardStatus => { + console.log(cardStatus); // true + }); +``` -RNCloudPayment.isValidCard(demoCard.number, demoCard.extDate, demoCard.cvvCode) +### isValidExpired() +Validate card expired. +Returns a `Promise` that resolve card status (`Boolean`). + +__Arguments__ +- `cardExp` - `String` Expire date of payment card. + +__Examples__ +```js +import RNCloudPayment from 'react-native-cloudpayments'; + +RNCloudPayment.isValidExpired('11/21') .then(cardStatus => { console.log(cardStatus); // true }); @@ -106,5 +115,27 @@ RNCloudPayment.createCryptogram(demoCard.number, demoCard.extDate, demoCard.cvvC }); ``` +### show3DS() +Show 3ds secure. +Returns a `Promise` that resolve cryptogram (`Object`). + +__Arguments__ +- `url` - `String` Url redirect. +- `transactionId` - `String` Transaction ID. +- `token` - `String` Token. +- `successCallback` - `function` Call when result is success. +- `errorCallback` - `function` Call when result is error. + +__Examples__ +```js +import RNCloudPayment from 'react-native-cloudpayments'; + +RNCloudPayment.show3DS('https://demo.cloudpayments.ru', '1237618734', '....1d3d22r..', result => { + console.log(result) +}, error => { + console.log(error) +}) +``` + # License Licensed under the MIT License. diff --git a/android/build.gradle b/android/build.gradle index 9796197..0ef7580 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -12,12 +12,12 @@ buildscript { apply plugin: 'com.android.library' android { - compileSdkVersion 27 - buildToolsVersion "27.0.3" + compileSdkVersion 28 + buildToolsVersion = "28.0.3" defaultConfig { minSdkVersion 19 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -32,5 +32,5 @@ repositories { dependencies { implementation "com.facebook.react:react-native:+" - implementation files('src/main/libs/CloudPayments.aar') + implementation 'ru.cloudpayments.android:sdk:1.0.3' } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index c066142..77529b4 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,9 +1,12 @@ - + + - + android:allowBackup="false" + tools:replace="android:allowBackup"> + + + + \ No newline at end of file diff --git a/android/src/main/java/com/rncloudpayments/CheckoutActivity.java b/android/src/main/java/com/rncloudpayments/CheckoutActivity.java new file mode 100644 index 0000000..505d1e2 --- /dev/null +++ b/android/src/main/java/com/rncloudpayments/CheckoutActivity.java @@ -0,0 +1,69 @@ +package com.rncloudpayments; + +import android.app.Activity; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Handler; +import android.view.View; +import android.view.Window; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.WritableMap; +import ru.cloudpayments.sdk.three_ds.ThreeDSDialogListener; +import ru.cloudpayments.sdk.three_ds.ThreeDsDialogFragment; + +public class CheckoutActivity extends Activity implements ThreeDSDialogListener { + + static String acsUrl; + static String paReq; + static String transactionId; + static Promise promise; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FragmentManager fm = getFragmentManager(); + + final ThreeDsDialogFragment dialog = ThreeDsDialogFragment.newInstance(acsUrl, transactionId, paReq); + dialog.show(fm, "3DS"); + + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + dialog.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + } + + @Override + public void onViewDetachedFromWindow(View v) { + promise.reject("Cancel"); + + promise = null; + + finish(); + } + }); + } + },50); + + } + + @Override + public void onAuthorizationCompleted(String md, String paRes) { + WritableMap map = Arguments.createMap(); + + map.putString("MD", md); + map.putString("PaRes", paRes); + + promise.resolve(map); + } + + @Override + public void onAuthorizationFailed(String html) { + promise.reject(html); + } +} diff --git a/android/src/main/java/com/rncloudpayments/CloudPayments.java b/android/src/main/java/com/rncloudpayments/CloudPayments.java index 269ca7b..99b5197 100644 --- a/android/src/main/java/com/rncloudpayments/CloudPayments.java +++ b/android/src/main/java/com/rncloudpayments/CloudPayments.java @@ -1,12 +1,13 @@ package com.rncloudpayments; +import android.content.Intent; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; +import ru.cloudpayments.sdk.cp_card.CPCard; +import ru.cloudpayments.sdk.three_ds.ThreeDsDialogFragment; -import ru.cloudpayments.cpcard.CPCard; -import ru.cloudpayments.cpcard.CPCardFactory; public class CloudPayments extends ReactContextBaseJavaModule { public CloudPayments(ReactApplicationContext reactContext) { @@ -19,11 +20,9 @@ public String getName() { } @ReactMethod - public void isValidNumber(String cardNumber, String cardExp, String cardCvv, Promise promise) { + public void isValidNumber(String cardNumber, Promise promise) { try { - CPCard card = CPCardFactory.create(cardNumber, cardExp, cardCvv); - - boolean numberStatus = card.isValidNumber(); + boolean numberStatus = CPCard.isValidNumber(cardNumber); promise.resolve(numberStatus); } catch (Exception e) { @@ -31,10 +30,21 @@ public void isValidNumber(String cardNumber, String cardExp, String cardCvv, Pro } } + @ReactMethod + public void isValidExpired(String cardExpired, Promise promise) { + try { + boolean expiredStatus = CPCard.isValidExpDate(cardExpired); + + promise.resolve(expiredStatus); + } catch (Exception e) { + promise.reject(e.getMessage()); + } + } + @ReactMethod public void getType(String cardNumber, String cardExp, String cardCvv, Promise promise) { try { - CPCard card = CPCardFactory.create(cardNumber, cardExp, cardCvv); + CPCard card = new CPCard(cardNumber, cardExp, cardCvv); String cardType = card.getType(); @@ -47,7 +57,10 @@ public void getType(String cardNumber, String cardExp, String cardCvv, Promise p @ReactMethod public void createCryptogram(String cardNumber, String cardExp, String cardCvv, String publicId, Promise promise) { try { - CPCard card = CPCardFactory.create(cardNumber, cardExp, cardCvv); + String validFormatNumber = cardNumber.replace(" ", ""); + String validFormatExp = cardExp.replace("/", ""); + + CPCard card = new CPCard(validFormatNumber, validFormatExp, cardCvv); String cryptoprogram = card.cardCryptogram(publicId); @@ -57,4 +70,22 @@ public void createCryptogram(String cardNumber, String cardExp, String cardCvv, promise.reject(e.getMessage()); } } + + @ReactMethod + public void show3DS(String acsUrl, String paReq, String transactionId, Promise promise) { + try { + Intent intent = new Intent(getReactApplicationContext(), CheckoutActivity.class); + + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + CheckoutActivity.acsUrl = acsUrl; + CheckoutActivity.paReq = paReq; + CheckoutActivity.transactionId = transactionId; + CheckoutActivity.promise = promise; + + getReactApplicationContext().startActivity(intent); + } catch (Exception e) { + promise.reject(e.getMessage()); + } + } } \ No newline at end of file diff --git a/android/src/main/libs/CloudPayments.aar b/android/src/main/libs/CloudPayments.aar deleted file mode 100755 index c0cce8a3ca506340e25c1c3b3a1780e7f790e4f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86100 zcmdSAbC@MxvNl?_tBYOivTfU4?y_y{vTb+Sw%KLdwryLtesgEeH|NZmbIbFFwIRz|#$DJKOA1_J~H1qB2IBmh*D0j7fu3R86R$m|}LFI3WHX8E}RXdQg)!zvSGL)k< z50^YVTxP#ECw^sNE`x1`iFlGw-cg$FI#&#tado1WSue0(8HcBr`nhZSEiz!Phj~iY z$H&d%;o9;nSfHhYofrux8pw>5+CwQJa#@OuHPjMX`1uQE{rQzo5H&}x7S)XBPaD?X z*=}OXesEz7I%O!DS$7e<+T4frS^E0esb|v%CV}LfJW+D0AwT?^1xYQBAYnonW+A95 zm{F(jSAMeHiS^vH-8mcwf!W-E;DeE)U6}e4zlQ418g3CaQY#NEN0@<_cCQx0Hmnsz zG?QE;)J|e|3QJ1aFd;vG5m(teC+auayx1bd>31Nx|D;O8bq=RUt2>|qL zgS!cdkHGxEze91FR)aNdS$9LTB3b%?IYSd(Zmd_Vuc;(BTeOz7lzHEAXuW$nzMSTj z*&)J0b@lgu=_kTR=R27X$ku)@q@&$q&2=@5ZG+=&(6st{IU zt~s>7=#vQpkbG`WFIx402!7fOTd7A0yRCRczr@A)qZM=y`<;Un(#hQIl-5YN6%8Vbh)JVr=LJm)_=)S~yDpPvbNt>ON5wwIqxeD17UIMQ=U znLYQ@m*O2F1a1nnyu1n+*tI*CMPRKz6NB}y&6MO*OC+3(%yxBT2Gy@J9x^l8eW1D; za_zq?NiP1G<8RW!ql(&>>7h1-*Bb=)mdD@@k0nHL=?uC#RUWs-WfR4wDeErQBloyp z!_>3qK7`Q9WgWW?WiT?xcIL7V57&F+%UCiPmiox(L7tkz{q6Cr3M|unbgipp7eDda zEJb2i++oyeVYKixIA9c*2BQ^K zmOgOqjv-_+O$fv0xJVnWAJV9%9EU|-H0fi;pmexUjO>TPjyjg=-8#phgfgKgWSCoY zJj8FNym!|UZkS5MS!u`vHkSxcW4J(b4~+ENi@^e~;)|{k-iMy2E}P*x%C*kdsLP=u zox5&AEqX}Y={T&T>-%4w8$HplY!W1bWMmXlQnZIvHF8?$=RxFkeFLba+<7K!*NOua zewezN2uZ!?9y?qO>GL%p|e z>U_!w7hx=dYJA0+E(O zPzdfjX}C+#Yl?~XN6G|CMPK`4qej3nZA>mkzy?6$Rys8vk7(rGfLKkfRF=!+R8{KA z)5f6O`&&slfUf9L3;o#T{J4HjwM838x%|_RdPls=@{CyP51C~8$DDQH3uIt+=*Xiz zSI!B3HkdP}n8&aWjFh2EEb-FMX=x_(3rd40V}lC#y-O(VEgGWUnk`xF*$lx^uvTgD zs?1FAFqL>(^)>|_rDzu;t0gGG`k5EUkXW>s-b_c@Gk0D?V}t8Sa6;qJJiR)|8%xG~ zep46T-vlt<%(2*pBn%%I8lr;L^W+4^nWnf4(`O^HAd*v`%$&IRCI+DNuMrSi4C}GS zM)1-^%jF*g1!-)fTbpREEn<^?Tc082y{eXs&%`Q70!OH@0RPPDmPHfc_9M_G=!sbzIfWuf9G4|+yid^D;vFx)z|SHc zOhEqI=Jz~$&!=J)qHwjEYXhS+Ae95({r*S;1~Z-~9C*)zvL(WF5!S=ACnP?X$-T-& zwY+_UO1Sb&Og8t6z;n5UAE0tdVuTeYRsw z=h~7^wo&(bL`hgW^NwB(W5gsdoG&mL4ww9n+Z@%sGBOZqR)GaY-}OrD&`W5;bRr; zDsut_3oK0E15_&I?Qif+ktw^Srs%*AI$g&)wa$*a`K4!#!)0(-7s>*ZPxvN3MB7g@ zf7VWKUHw=#QelNv=^?R3!3u=MUjLu<#E}?WiI-&N~N+i0quE|o`8q1 z1F3*Pm})gQ9g3LRF7~wBru`xoRV68gXhMd!Q!vE+ka->=?cXW3)3x8DH76CMOam95 z(O>`$w|o(xU{S6??c7E+5%UpgH5O9|EnTBmwvWoF%8-hU`+l?BVn-cldj)*X1|)wm z1aTaDYo#Ph*(Z~4*Jrm_bnO03?Ed!51sbgcOI5qu9oO$uO$#04y)>5a|_tw;D!OKsn*H;ow|X60EpzNNx=Qy;p6ed~w)ztdeiM-F%wU#~1oX zvJ|sIbGf@M$)8#+aOE%@i}#=RG)o8ag27$US(atNDs-LAtD-mMj4m!6b#S(U<4t7n zP0m5>naDx61V^g^y-HJP618*m^lOe9p?3U!)*&oq43JR}lydu*63CCSoxkeb+R#R^ zArhLSihDVuI4YR0@rXt_1rSRTUO24hEFH=>(AioAhpzp*jTDwB)52q$UPRLCG)T(r zr$-)pkRc!k1C6p~)QC1Z-JF#%wIGRq>3D>EGdMl9Q+O-REH(#9{E3an$R-4^&zir3 zK1nWeYGs}#JtQMJ&*zQAq6ABHxJZ*;sKat@E|*@3@On|jFKHp@8-$vPr3|Y9?ppn$ zu6@yfSUE92!_b}AwHr%c)td`C*6*1apV0?T+EaJWXlzv>3d}-#j=}h|l?(fqQvHk2 z+_2TY>vAj(1I$YcKAo&7{L%ndO7G-|0>Vl~c(CY4O&)hc@}$WxS1`nyNj;5{)(lyOhhoa4T?6=9i!q$J8U+94!{@I4SSZ|)y10PZ z0en*5WUS6ov&37D9Mp@4DEmG0o~9|7-!VpC{pS>w65>ca7;ZR4U^af`@@AA>67KwOnU^)gW@;Slc!)^ zDX&n=5J54rnR)a(VnEwg|FKmC`pQpl;!CMryD;S8qX0ZZbNS(kN&eDEp-fp%g+m`& z*(1TKd&p|+sk21PIthI)Jx3w}wj7LB^O2ovTS@A$2~c8A4q+rGr%SRy;M?RV55BYc zmKYk`v7mMDc80bq>XM!G0;28d-rZ+r3FF<0MH9iO==~kO->6L7hsOueHy}X{Y`$UM z2L+Zx0Y-agd7Pz-$vmc2n0!R0d4YR<@Ctu0duKx^TstnfGwE%IFDQiQx>(~LO4%$R zGcD&Yrs-LXB00HxQnKW5@-E2EX-5LEk5BFF8V6kNU669NSMjid1`<;-k7GT5inNeL zz&rIh&4-`UMP`>Z*y@F5-Zwj1HZrlHg2S&GjB!3-1nx!@l~jl<#814A5TOvU0mq0` zoR>}sO!v!{SoQ{IDKtrIN(RE2o%5uvszM~1E8z1T98BV76KrL^fw6>FE$n-&&|AaX zq?M!`th;i7;6Ye$|Bg8D^X*z1aVvBa#kGHfc%R{m@>ghO@&kDM0+&X-b@dePpFHMB zEk1yo5mgbA%VGcHOAKuhk!SI%;+m2x(HV!Tf7lDp5Uwn<8+rI3pMyz{9^#rR{9c>R z&=Nq9N17;d(7~MuzB9SK?LZ*$q=@o6Zfh-d`SCCu5MhWk{yl!h4=@~H)Pq8%32aZi zBrFtE8HGOLCMfe#L9Z7_Nfc~pF4(% zfdTPAN(+NlHKlA>u*N_S*)oT7tZ-`BzKI_+y4ee@7_2Ou_T|zBp>6Rqvb~odIm3x8XrcLbZuwv za~ZJ>VYku2AS942`G=y!#CN?eyb3*e+h+8y{TuTje%V*(POnYEo!VeEI zEOJT0XxIZeL22)C_gmI}dGE{R#o9OJbgFho8b7N3MN;ym#6=efINEtal0b&d6cO=PTcx|)%$oha2o-gMS2i}3x|b~Rl!RL5IaG5PdM;}MQK$C z3lmx#8IIriDSY3ma+K1o?5elo9FfbmBR%Nk>jf5OGffjr8o#A;wxwNgFZk zKc4f hV6aH|du`ueSebUY;pe^9+-UABc;DjnJ&s8}{f8&)J=KsnSgriTIqMi$(+ zAYA!md`l0S%xCRA0L3Ig@xYQE1(LSg$Vl9 zBe{&M>ez54RX@Bv*Hw9X4Hub4^!m9e=}QHpG7n#<%B27?aEpB2>Q9d!imDtR4COIE zh^w1@%rjTvz!`*BQI9R^yT?QqEk&ghPY5t*fqKbg`}ROAEMxiKif-Vo%cgQqr=$h_ z0uD3Z&p>mRGy9`M2K%^|S8iE*Pz@`EJ&h68>G#F-Ni6785ZL6aA7;-8rZJokrGkO> ziNw0rT{I`Z1 zXbJk2DilP&Un>7j2lOi<*zH$>d(o{PE6PJe2pLfvTa_WLaVDOZM0DKRSuD|he1m1l zb1`^P?T5T~O;m39;J8ylY%%GW=SF&6=qer^odC{Sirg&B9exnNbr(^EaM4hbLOH43 zra%Hd| zERounxBs!xF)-4lhCs$DBk$pP8om*#=Ek4e?gCN}Uo5N~+AbnrE6YcN%VJPF(CqI<)>MyIe_Q_$qse`6YMTdR&I?}Ztpiqjf9HNX_HBm_iHe~OlmQ=*E>AYITE$W`;-A8@sPcU!At{6=Y z2eu3pgH0P9^*OH#?n?LO@CuromrD{4QBU6zXl~TBcD&l;Cb#>y9SgguA4=?46IBY$ z_X^tb(hmNR$`@Wqn|N|f6)5yq>`r!|?abC5LbfvlKR=X5ZI^W$$8DEg9)fd3%wQvB z{8K7bh2moK$tFr3TTZJ@tgA*8o3D7I-uJ1Wl$VifrjWR)KFeorN#g|_*A{-z!n(>J zopd8O#T^%eqqYg zmc6ANc3Ll`UUqu#P?poAN!2FH#0};8^$2`_S_p|{`N(+tW#2u@=6y$NW zv$0OtCZXGqbk%JAiDWyzs$M?j2&w!V%ui5|&Vk3e#wDh#y@0$H@>&ML9qC@|k$nQV zSCWp25#I~dC=1E`0y0xe%cQ6$RL4mqecYXlUtvVwkoBIm50Y~9fXRhPJr9J-O?}F(|`T5*)Zz1=Z z(wch+vY44Z>%~f`h%P z25KN5kV87xo-Qzu57}%lmQZ%!0H4mL&)}Sp_f5C^q}ZoRoX!q6YM?fqtA2X#$!q7^ zWACj`+!vMo{g>}|P}_V0pQ}VK--C4MqIYfJp8G!guF5h$>9`->JB`cvUPt*5X%XKn z?&g{|7lU1072}M{5I9BYPcvJp&69dmBelg;fY{uQ9COUSL^Z|cTSyZTc#Pg~oZ-trr(Hc9^ z4a5dhrf<|9dsmG}M(Y<$cht^+u!S-;&Ktq74f+1_>ao>Y49Aol$r0;gS0VXp177OF z4-vvB?G4vprvd70NojJv8(Usi~&*c{QIu2@wf23y1YSY%tn@>m92 z;%RXZoD@?-5F81XI1n+$-}_y$9ixpfM~p*#UD%*gLsLJ5ro=5{F_{@_hoG1l8;3BN z<5Y=(Bc-Vsfg|(LB?O3;2oLiGMIoR$$j}&B#fQGS`u^5M z`d8PzZ`!c|+g^GfM5rDSk$;3Pk%51N9#N1EmTdCPz zoRs?^f-Am9u`L=}Wn}ET6qxnT7owH>mZ^a@V+7jKp#apV4sutK?;qm3c5nP~w%xxt z4@i~o84_71O`MH}r$mn`!0y^w6~8qbo4ws-mPTosG>gjUD4DVr?dY7swU!deeJfzp z`1f4E*N-e4jS9zB*hc2ilipsrCi$EXSu)dq7wAZmUcRK8HQk%VUqj4a@kQg$i%m8w z*Ei}Yb^DUUXh`q_oB9w015HsQ6d+{RQ2WvHe@|t{JI>(JZScCz+Dql*l1*4SyIODM zMKK+zz&!|jS9lm(PY~D)H+jFDg_&%+{c!j8l36&e2z-v-lRjO_bnG}9)75xzBti}I zmBf_?A&9bui~)rM2Npv;7_GxL@zXn426c}wTkEtmM1hc?diG(#WcFL@>njFBF#!HP zuveSxS+NH7eAt9 zSv9aRLGpG0o3P8_gRL=Zy}rlILKK#IMmnr}607)B-d7A!0O`^fNK`5-9Xm5?)Ak|#*ph~oJvr|hu63p%;L#x7)uJ_+-B<583&h()mYg z!Tn8fkG6G&=UBxA_21+~)LRn~JO9 zsR^&<`WdGt_i)7hh^|6(w{epOxpkrY6*EM_jZMaYC7^iYoMUT#x%4(Xqfn_%n1w%J zb&8LMBi%-?c`J`DeRwzZ^phb}`RMg+9iJT5PEh1P|KGr7^>1KnLA>_m`+_Xxzk}@` zLjO0gar_Oo*V&#)MtXWum%qd2Oms;I3cLWz8_eN`Kz@ZIH*1$XJh$98KfJKKP((tb zHhlDZX-Q|j#l3x?XcSPiCL0>JucPjE{yc%&j%u>1tnKnEUpf9Q-`9?@4x4Kd4hwWN zAR1B!H+pB?#PjuWAqCa@HNztGjGEIYU;nUQ&C?lQb{1vx(LV5lS>~EknbepS586Kf z6h4AqRCp!mi~8{9tLSt+FZ_6p$+&gnVrPS8Vf;~+QZx2!zmvm4D@QB*W-k$x5L&*1 zMTbtDfCABR{s%3X0!ugKWh{Bbx14Xgiw>~tE!vs1IGH}uE^}lCYG6k=#HGuaaNcxk z)W1*12G#G^=0yM@=qBCV`@Z2GJzb&vqqK{_HQJcJ0M1e)!meXIQlw`ZMR0J)@xd0HhTK9wbL8OWW9vKS*Iez`v5^4 zt5+d)#L460BIdrGR;bJvZNvj7S|ks_kV@^0e08JdHg2LL z?Ari3YXH~XpT;LI)_H>;g((62FmQxO6Q$s^MnRZ$#yz^&v9`WoHT>1)4_Sx`QGcqX z1%E&!Q98rq=7J!1fgvk{1Yd*w!iwfDgqa>v?CRBw_d$~=7Urm_9C!;b+EUuOf-}S{ zPn70-9&5o4V^Wcf3t>W(q?Hx$tq3854HLc)q0GrXodViQ56qo8C(}5`UB=Xt_O!D)-tHw zYe08))Ui|&Iu5#P%bG)E$_R_YC4;R1>J$gus*riX2}N1SAS$qhnblm^ReoaDu~HKH z`8aD%)AaLw^Z0#T=ikR`=>OHwUH)CbR^AAQ{^KiHTlmTu=>M(MKl{i(1@`}K#K!iw zh|P)esKZECH|6y25nD#2_KNQhD-0_)_o9pSmIu)}#Ms(ulOWpfq_js{Gb3__+(X;n zNN_*Tgxi%GNs6x9SR_prR*tb$HrLH8<=Thm)ZNcNn#%ZWN-Lw!s!B^&IzRIl_{b~( z*F}h#mA_wK_F;SXN5^JAex%#DZz<~L|GxN07)8Z&>H5GOr!Yt&lCcx3)S`c)X(J3^b zG-zi1cfz5p*M{f&a=lZ6Ex zEgY53$B4taJTH%Z2zO6sHkDefkFi_n23%v35WiJ@pr$~E3SZ<>gqYEo2n8nTr9j^AkhOAuTd_$P5(4Zk+4pl5aG&sc&#w`z+ygVAO zNt4II1v3lN(MEK&YL$YY7TI}rgTHIRi zBU~4l4o1eP>dh*-hsE<>yAGIof5iEwz?(x)WqYD*YO+lxP*Azq-R=o>7)?IU%&pD_ zPLTDXF?4}EbYlc)=L-`E5X|{N952pkkICC)Q7J0_s?U4gpD@nGWzw2(ztsa!J%sED zM2Y_)(^e_LUo8E29&-4YJEW={Jj{Bw^s=ZNJ7JRz=W8G40G955H0GXEfe;0o+QFcs zlI68@eU7tRwtv97-9vff7gkmVmD|HY-_C$T;)tPt1c||+mlQa7cJ{vQGm zjDPXBf9s}&I3XxvUk~OWd_4ig@bAU`_kQ}7VL1TnA+TnPzfnCJz`<#%@Mz%(QChdR zH`ho}DCE-Ed8U4JqoQR!HoBRQ)`ER!yELdrM`xzmLbK3VAtrV6nC^4cwn5+Hn{}P= zvX!cvU#Rd_TZy#6iz*(*XnIL#T^767?6UfkSu{PfQHv|sD^ zFg+tXC=%f^z{!#ee9En6GR_d355R1<1=c`pKVHGLr<|1yZ@H4?e2=$4ejP3nDaTlr z)g$^$;u_ftW=mYSSbZJbJsIg(ry2CUNVREwGR&ktZPg(htT6xl{6=|r_I1Uf4!7tZ zQ;p8vu5^j8eMd6h=d`7ewSi7xwXbQuyjE`8As6c_;0$j27$LWPFK;)cX`|j=2@F}* zS~s5eX{R$17src+v^+2v5aHHIS3Z9=>>OL{b>{F%p5z_sy*n2O#Z1c2abu)54{jd0F8_hBb?p!a}9-` zR1RbaOcH1wh{VTSJn_jJ>*P~*%;HV*viQOd@|mgjPo7sx!sqH)vF9pmxHD&xcjfEc zhHs&FB>0&ff~xryW3u zy9>bLrjELZa)1(n7=8aX*5V)!XbKuzsOz8LVePuC@M=X}CiB-%6$?R*ACB-_!q@MC zSsymPEjyww{WGh|?|I(WsSz;;T_4{r!ACw9+LzrKjNI`RUC&DmmtWGd^bwVlxI49R z8V2vS9m;N3$L-H=}0|{zYz^#gequFaQE<%mkAMS}iA9lTD`P{OcMLt{kl)cth=Rd%BK3%Prmv>vpeTULE z?PT@Dq`d~60ZFe>d@r27ybU@ezUk5k=rQrq9|~+K{KSmEe1t}b!H5UENpwwq_rnHu zoR=Y<)xHj6Hm;rEk}IBh0$d~aW8XIbN#0EJ?cVN#6I`&)Dk~~2iyh0E`WFz?%gq-a zTFWXgWcLmnhp*v)-ANO#wgr^Wk%Ny48a`(n5$L|)HjDzb!p?*Dr>gTnmO;5Q`JU2Dc%sVYD@4hO8g90h~&vz?oh39-D zDqbD6Hp{c-W^%)3AKLHlVIQ5d8#tp&-_AGwtX`Z9bi8x$-Q%rXyu7U_JZ_XDZUC~x zCRu-PMq!7ze=AGH|8}n2=B$&Q_@2{%JyqnbLQF`8D$XGEw^7ifr1Ot(mt%nP>pKB= z;oO@F)b+PJN>_RF=tU>LnnRK+dMxjCKBshfx6imXRoavcd%u+zzpvMI${KcBxA7fU z2G}fHMEaeMR#bYXSGwaX@h&dY@?q0xHr)VZ9`;S5`{h7t#@9%62#hnHV>f5QH>l=t z_(1MTJjf>rN-VcfV#Yuzoat&!fC2H+phr~Z@NzhBPxRpI3nRnSRue8S`t>mfv7&WlZrB-Ay{ zM8EgVC{v;gS`}b*2~!If>6@Fvnu3&^tuV6pH=5^84P;zMsvrYXJ^4#e^TB{A2Cv_k zq>(TLSZ5Q03C1O5wLg+aN%s&OCe zUNLAjz6BhfX-k{?6^YiOuRD%&J8WriU%GMkBSLYTJnBn;!6Rr+kS>;sLdNd6F78sq zqFhY@oOqaDu(p!vZg52oV{F;`K%!fByXS)~!`&1}pA8DADDJ{<`wb zptfmhsPJXP?N0j?GioSpVW2#<?3`5A5l!Tkd1 zd5>v`Uw(R!5Q-~sOStCuBnsA1;C$GAC;f)oQZK_TQCH9ksyJh2OsGgp6W~DQt#CP4 zF>$n10U@+;+rs*IR6!xquzY;=1(^Ow;=?j0BEadMXNCThEG;JR6%@5tjyudSckJc1 z%hm-FzqJ{XVTiadwH6_0w9_RJ1YuW-XBq*H1J)X7rF}a7+8K~0HLN{Nw@EjN1Q2xy zP#W~Y8YkoxJ+Ljtgnye)5{R*zI6v&(yblFewzn3TFx;87Z?Z32Zd!&elU}9;^!TI3 zorEs^H|QH~i|OpIhZR*qZ1fBwZfpbG5Ec>F_&+KD_tKi=98@e45=}H88bssRwn&U9h9WpniAXd!VBdUgjp#BkJFMW4Tma9H#*0E=q+J2 zg$z2mEZ+m30q$kA!ER5fw1^b!I_d+C>`u+kMhGMb?Y#9?T^rg(T~&S~AS!P_^&zV5 z>~i@IB1x=`oEzJGwjARRW4B%K9lkuQMh`w+pO13GKA=n^eK)vQ__FK=A;g-}VCFT% z`l${ss%1fvJ`DN4F^{E3;57lPIce*eT_lA1bG9)Uy4B&&ow;BUxZvHJsKJVZx?i(3 ze0DDGI-i}e>aW_QsX~?LJGg?4c)8Z^pQ%ra%dHJxYa==?JlbCI?jK)*|l~WM84Fs5D#9 z%qSwKqHJA8UV_BVy^n>i$FBHol8mxUT$6V^9zSblJ@8-7$<@F%H z)4iclm~mb21UF0$A+9B30fqJer!8W_TM@Yt2P_&GLp=lX_+o_v8)!6%=Aig!Le24= z*YBBfmkwB0jvb~8eu%)W+jzD?mwrfaL~R^%p;UhauIoSwsb?*^(3{+%sj5ZwaJ!2a zZ`Trh*Inrzj8tKj0(DH@uRMuUnK5w7%}?>YIu8#<`PfhLz9GQx!qL3q3p3DvsCo}m z^&j`YEd=%8DSqS$iokX_&*eI|*moZ?xiPDB-ZWxF313aM? zsKN+BKS#I#A4SxFA`p9ip8!e)n%Xo|`cfbGDnUG@eano!o*HD)r-%YmbPQ08s$%QE zV|Kc`ypj>N{*RGbNv#@?#D9LCkyVZ$;l;Urz;IF^sYfm<6Yvj!X+5(*BhzZM7i{tIY z8@dEz0NKK@9zyu`s!@%`2LL2mg>9RA*-~emD@Qbo2fsrZ&qa(1V?9iUv7Dcp-7@*~ zveKgq%;oZ`V$~=2?9Qc$G)^DaXRjstDl=_P?}5=Lt&I1jk4Q%6qQXNH^v-7NQmuLR?v?lhGTs>}(NvN)fm(>pTTB zQl?C#&=PSh{1m$~?zRJRawx#~Rv)L+PI14jBu-m?Pq)}cz~VZ)T>4YK;Dbs#Nr;CL3tSv-4rqUX3mZO z>fUwFojsTSj=wRL2?aHlx&irGbRf)&9m+R)!uFpv^3QYo2Zj4T&Q0e(>dyZ^nc){; z|52F#>rJn}iuZq?;lCs9%ootZ?!VZ8tFQLszZdyuw7;l2;44_fyHL5GV(>87UoV*f z!Apn>D}3$6|7uVWUt8t#IpjG82b1(aM?&ePrHlooLZr&CjSP2Px56)a3}v_<9T zlF12L$giO>@v|e%@Xqm54x)KyOjj80RDx^=Srz3syZ*5Ta!lD-I1O4IowS;Y@0jXFF86Vc@E#a4tb$qVQF$m>H`v| z7`rfX35R7)gU!m!k~_||9|~V}5z8%~9K5&6b@&{nw4bs)71MRwMr(EbM%(NJ3pltY zj5ZiMdg@tfRvI0RD-5U>8o*j}3Z)k_`x=ikmh_}f`l0}Na6DWm2X%Q9#atdfblpNX zc*)wAF7!Do3-PMTlIG*9ySZb0^D&9=lIt1*QV%MpE3@Az&89mjwYV&o#3uj#2SJ?X z%R6OKXGTxmPe?KWUbN=SGlR+A`Qp+H83~&a5$I?mtT-X9v+L9=y z&JnBC*@Vym1!i{8l+gaj1`isg-3V6%Ks zCaz-|-TbD`d_ktG<32OPA&Ui&Pbye;{tO6_J#>JSCVYdOZx;TFlsfBC325u_bJOF| zN|tqbo(aOj>3>niC~MQi{_q|wJ;|dCU3TF`Q#M0?^Fa4Te=ciP6U17-pwBb#p>_A==yx1a{}kje^ETR!)!O zA2d6LvZqg3)u2OwdP}4)6BvbX8Kl+8WwqgGCfmGSg>)Q^Ik_w%V{g*vxp$*}jNF*? z_qzW{v~Jl>M9^TrU&hNLg{n!qPRqKYm%nuL1N)L1y@#{$*s*394SnS?T6VbiP!#K{ ztS2(b5CO~t(cS&l=_T&WjkgViv+Q|cqtA7gr)J=aOGEtH=WfX39)`W7SI=6!-LvVc z0c*u$7x9aEVFpty*5iV`C=@}Hn@$~mA`-Ud`bh_D3YbM4bf8iZe@F#OE9YTtaE%hD z=Q_IcO|a3C^bBy&`I*zi%Pqy$)_q2mA(Nvxi;nQwZ4KeATLl4HkqD1W9u7HKWwY6FY3tGA^qNTC zi*Y_DxGpC~^WE87_5}|^28Mb}%4Y`)DN|;y3c1~4lz^$*XK#0%P@B*gQ)$6&Kp~$C^aM-FQK|z<}T*a<1JVaP+@mHtr#juXjq#%sM^2gt4G9K6rJ5kG$e zA|R9~H$*`khVcF&v(W^XhdLXIKmCvBtq+ai`h2kbSfZ?adfC$rP6(tcV?3JUQlV0A zq{*9+XzcD+r$n_Ri^Y=S@C(#S>V~M>f@M>tVaRE>mTQZ9I`N4EmnibXkxbJ9&-j6o zGZMhFGiKlD%dnfWfW*wsAxB}z)qAR3xyofndn%Z-FOsuO|7eO#Tx18!N3a)HSGOvX zUTQ|ebn(uL%ZN!yoadzQKeSdAbB3qwg&gH|JHCgJHV8Qgh+N$t-z zRF;z5o-?v*r}{I<>&wd?fYRJ^%P|Imq8=wlp+rCxc@C?0SV?La91-`&h(}FJ^jO(R zQnwh>Vnvx+{c9a_R&dP%3ran@8v#!VOgwj-^;<`fG)%6gY`53osg@aAUUF{I1cs)O zSgwc%yY3fhfm(wuMuEZ)+V{>evfrYid9-!ZlQ>k`pW36 zaeyZ;Bn*06jKdcZ zssLMHaZm*HNZ&y`5(3Bi9v31Yvo3+MSfG^m?WLFR1jy?!V8llCC_W?Mj<-0#LuNDM z05F0i3DhW{K0xIYf=`>RhmVVN*ved%wl^>_&=g2M@(2SFBtbhjWGsw-4LS}`%J_OK zZVU)QAMZUMe@r_+NS9p9E)+kfYZTZ}dD`I{cMq_wt%3#_y%3tiiX- z4RHro;Qj}-vRLresVJ6sbBc6vgG|{buAu%(N?rrZ9@#4$<1MJgh;va+40bNL!MB zX8Mm|4rdu>_O8RwK-vAWDiVrQ_`aC#D*2SHsJ5~xjT$$8c*HXdfYwCFwTxwk05SezeRrPM4#tCjlqo_T9 z!{S=ln0oYe`$F?xtlgd?5bx<*ldA#jE4#we%DFdlObRrAFaA%LHF}1_`$P&>Ylc4p z1yBi38!%blD4Hk&A7iddD>8ATwXQ5B+i{^axhK2P?Y~Lxdl*wzImgWI!Qav%AM7S< z;|{t7{)P9nr0Yk?gJ9Td{kGCmo?YFRfvWvYb+u2Td|t;mpI9~N$I&xd8PTs!CG_SG zXJupy?UD%9rV-p$y#{xM1#GqjqJ_#huh)e|$p>h)s>W5N*9Zf5oHO&Z$&QQ13)NqJ zWmN0P7G9_GlvTf4cAs4IbWW)N1^S)|?kH!}$7@weVJ&rtSKz$Ro1#%~Kj}Z+^~9G# zkT>@?5$kf*YM#_Re5e*qrCd~9o*o0D1?Ya0@zHzi6mRo|U|nxL67}Mr6b}WzH95}X zErYweAl>lc@pfT6&7poG7!DE0n?nB;FT;iGe_~LzGr9GQVtqyOKoTN9g)0Sh{r+<% zMK*w`RPdExWPdgCue1J}Jma5|hQFs#f29uDs5)o=i_`(tFQhjG{Y5Ot$r6y5?j4^3E_W3 znpW0aoY$gL;ir5869HGUwVDBRex{@t#{CpT+1Pk~|LDwTB0KarubS_7yepNhN$9sEA>jfn?z@3vSCeSdt4yvGaVyRfLiX>v;_1jDbx5-Atp7^tB-x1jzPEhR|)3CBeM*S?KGvY_tzOaI)E@%$&c81gx<2rV3ix!pQc@f^Uzuh;`sfE1AmEKoewIE$r zRa44t!0eb!JYa-QjnmW+mZ(HaURBbItYTfUjjD7U_61z;-2SsPD5UXzr;`IrGr;EuF^NgNn^|dOR48EV2hSY0jP-jTGLk4M+Xx34N$Z$ zNG}luXpcbD2%vWLG>3rHH1}J##LBfdrCxp>TtWUgs3&F~^98$zV?N?W=|fh`+)P-} z?3Ibhq8fk4Vx$;$6No~)gaHjwK6+JTx3-5$n+=t1h0r4K+ruUvCLnNzHEhc!mH-t= z+>gxVauoj|dZ9y0E8v%9kCfmpZm|%ZxVdzD5pii=nTDp7_*kcDvG->Qtl6A-`|zlz zTw|i_Hj&vRqThCK=RuUUkQ$M26-}-0cFcs1rx2Ajjl{jcMJ_VqjbQ}aT$eSaf1o+ zWHe^TJ^l*2!Fc-A75?gL2b1*%VCz-&;W%+2?uKs1`rA2(U0v>o(@J;+gc#=3F`0I| z^Cf_RwLU@)qX|UMa(8A(ZX_U?FXfH}z+n$*^pwx=IOq zH_WYCl90V9>Uq1t3PwZb**Ui-nV5VpbAt(?n-JS)RAH+S;s%Da$hG0Jc~H|f^-D0Qm?Oi5 zmYbq0G%K?OOo9c?W%jTFNzhC-YElnDzDi(lO3=VW-gyv6{mmK3F4dtD)aT}<$PDa@A*KdeA3&rT`fMBnDER!*O*wt|*>-Z>lo{o^rI(sHa5b;f|;$B=6XcOiL~QdAebKSd61s zJ=4QA?OCrY(ZbmIGWM;pprPM1jYK?Qm_GlwU`&EeD)I#)hvx>t*=3GXBA*3_uQ z^%VwRv?hl>y5#i#GM~#Ax|jp&<)bjXJHDg5fI8-?|qyeBOu z2!bDVKhv8Mhu#E>#rNNCvx;aX1z1H0NGFj@Q#-ELw-3g{I%M$4E;2NATcgzur8%<_ z<;@)D)GP5gue)+cq4fOh_1F?ANz7Eng8UaXIg{qDagXQ?Zcq2BiJ*it6&ZlX88#vv6&{#juTK1>b%<(epLfQtEJv z_3m5xxR7CunxLqwNw$j3&VJwW8$V=QenKA<7I%W1*6vARm#2J*in9nUD}j^#$?R8> zl1Rk|g}jEjvgfzi*UfQXl++fG+%KH%ZP*RyIb;9e@%qdnfo~x8zV4TJ5Ods5=nnP3 zU{*TQ-5gN zvB>^RBoO3j8OfTFIE5q-wT{Ei@sXL-^2C-wFaF!zOf3G7Uq5h9&e%yL({Tn1HAG=?q`pNStPfG+_*h4zCMT#5px4(`Ok@)BygRDd#Bb+qTHDu3XP9+!fTNz|V=;fuF=|KpabBmA*+698?`7we zeHSYPxq2KZL#zD>BUSUgPR?W;$?orFMV`83PDsd+t?}%F+~!9Y{=32pmmOoW$~&?7 z>hE-0FgV>NT4}4Lyu0Cn+NqbnE6ey4ttXp3j*k9M$e^zT1VtN=$sxnDSIVI=(l#Ud zFOOT-D<49djj_xLCzwv&w*q9L1fj)KQI3eDqILPn$py)j&j-a#h5GQ!Xl<)3A)O$`o%tLNx*yx0d4}VfXBm-=aHJkr#rJv$ ztz=9KNP}O|l>!;73Ep04#|C&MVyH9v@ot2!Owz)2B2CYyvytcavwi69k^54glRpUQ zrS{B`x3ptbByDFKD8_RRfx9pboIoHbBqs`K`~t;Nu@|U^ykN}esSW;*%DQO0_mi+F z?6wUMv>_}NQFOCWBj|ky)r8WH@t|F+Xw_slGql5VB6?uXgL#z6kd-a{6#X?zlJ2wS zIGIFj@GIs1G-;AF`K_OFo;G-GN;=hFxYtKvNrR4%x2gFoc317i z(4H?bc$-j)7&flt5pO7d10vm$x4re?+rKL|h}JZxL6Ruq#LK_?o=El49_jh@6hn^{ zIQye0OM|g+KqV#>v!1#4(Q==akO-g{5Z$Y7HQ zqD&M~@a%=Ac10{cJS;^yl+^s;c%fu7&<8NPaCi{vGMdlx8!0Q~ff(^x%mo8lj=3JG zWUd^)NtfN$q#ID_CmlbI#Db6p0KcE7TThuVQ%62w;yybXJ4O?2G3C{thyRx0E*3I6 zCv?OGQyGIAL+1A@GXGV$1@C1)xR(w39;rKKw+jY#hs}9^I1;{F^~=Zed`~cO87zeZ zN|87WMVXjHJOCGwm_j@PR~E5JViOoxkGGGgERtojD;sCzQ&?12_cLvsEwXT4@mFD7 zfB}}3%#CG_+5tS4gf5b>4Qb#;LeR%KSR2u>th^7F98Ahcz>t4Fcnw&>LokMkRsm0c z-kTEHA%0G(>tY5z&m(->)nyygO0((|!M=;ftFeo<20o4u=y1G0tfkbdFfs(G3#bmH zZ8fc_M5;GyC>X8~iUaT-RGNlCzhyCf6*Dv1-x@>|(zew4L-u=Kov`aSIvT^jRDHCB zscReaisQD$)0hppgEKOCMoVskGh0Q+rrLSmAK@o>m@dBIU@AA5?cr4uImv`xygj=( z#VoFy-S*;5E?V;@G-Tc&#DmGy7mZ;(vMtw@$f%t2{hSO&24R^}Z*B_Z5%(#OOIKiv zKlINQb8D_m4s~QDi2F${6ksYuB@lb?CsGu^H$#B(ro2} zm~q8!WG&31LCa8##(6+sEa@5_x+-wUT=FAs zl2|4xzx$lh8DI)N3Bm!jgUu01X565#U^=^Gi7rxr2W=qi;F=ctL#o6pLnqDg40(b> ze-MVjt(%-&HwIbZSjys_JL1L&6zvBzIq@S$&8#H^QZOaiKeni2rS(lqW=D_22)`Z}`YmcpYPonM>6^|N7T)*sgxC2e!rhmNBRtb^#phqtp^jVvwj%*ct;qY5 z@IOQ|@@dP=O}-8#ZZc6PSP99{b0jk1;C@BvJKK&c`QMwMVh5uRN)!~%^vngFFK9;<0JwNuLSR>5a8Jn!ld>)S0s zSH9WQ1@SE$>-n@jC=lX=^L?#IoYX*37EEOX$}#Ck9ITBb48=GVWg+Ju(}W1_G&mxp z%CEFY?en89U5nrPe6=f)@qB6J52Xg=&@e^VL2+I*s$11-Q$$iyvc)5ZYasX6Bo-iV z+2=O`3#4{$R3wJXs9!2>ptr0r*ul|zD%jf3Iy8{o-I)5oWOpzmbRGVt`@>J8n5e(m z*o$PV{cyPhTO8CJnDAqkqZkW1nNn1-6}?u2KLHdP^+f($uq_aDjbGRv6PyrQI5$W> z)*RfaK4=7#L)_brVX_5IGUL`Oa~y6rduo-B>KG}L-4)>NcK-AAD=^+HDmz!W6gtJF zb>{86ZibX7*}jKhV`ccrqU5MyXHb#+x5O|F;(*z)%DmdlZ>2k><;Xlyel&@fmLQ&p z1Stvu?vp96=1^SM2tvo2bO z2&kRJ-_{dBf|9La<9NeSQ-EtlLyPyr5P9Sl*NBzDAh=3vo=JZ$_&J`#9hmms*#o}m z^i*uV5rPEL} zg7sYyd7qdr}}{(E5TeJ@8!-LVy$ng zryF;UHttoxuZXCvp6L#$3C974!+w$JA`DmsOhdbJh_IwVDjF{Y_IO_Ascth_Nl&en z{>_~i5vVbeNxSjKYDB(F5X4STUGUCPS#KTB)Ztfzp2Dr*9H$Fee1d|Kgkzx3Vwjj%ja$FP(gd0# zt(fhLn=inv7<>y`wL6}9LZWJ+C%FIn^Dg};uRJ#?=Xz;kL*|tN=X*~y&BTXmq=Fl; z*DX6MJ*AtE$>?2ceu+m5?|%E|9n$R~?G@YIi@So>j2YAr6f$~t+BBa=Vi}mTnqNPP zbl+R(Et)gGKlP=H++$27NcZ+}to~I>)x|j4&#P$S;8A@a zhbp1*@0bu+YZpZ?t{ue)YhbDO!SgQQE;#}f?B@$3(FSSGR$}^+The>XqtXs}%INpv z?riLg1an@j?~VJ5N552^7PX0YpQ+vVOuBf@2||JHm=HAnCJQ#b8+e6@8!Y#Hs37J-+;59xDU44j+@RJhYxxR($6yS%a-%xp* zme^x}?}DvQP4}T=!ETs+;GYAqsN6R@=!z`?EChEE{Ho)Ze=_s^d}g>x>`JdQj1jEQ z(EDOI*=hIcnJ)3DWN2nSTe1V3V6v46`2;3ll7~0?p`>XQ3CjT>l?&Iqa`Wh@VhBn?cI1qr#}?B!#ngMB2EFLd zj{&IbKU6*lT1XzzG?RVDHaHrIb{H=RWxXU6ex1Wne3X3ZEqp5DPlD++= z+lDbK?4_Lk-5dn@#KAl1gwz;Luz&_n1~N`C*ei@qy+<4T&fPp99tKm(BY!Gio5VcQ z>TJ4?7Ts{;APaQQy*W>@rZi^#8p5LB=G%fSFKYAbF!al)h&aEjiE~S==USpZ?9bK5 znIXW!X8IZguFi)KE}rAO&}DsVoRcP=ufYc=gT!`L&&Lko7u8Vw-Y*5f5<#BuW$%oG z<|0~Yn%kWm7w9kV1-Z)S*60mXx0vZ&f*vmj{iHSPY7O}M`n+BSjI7MDcX3?E_kc6K* z!j7t_PXTH+N-JN|xELuU_<>7r9fy;yo@4H%2gO{?WVwiKJV0v!aTvUbT=(xZ^uV%FM!T2yU}pjCX7zy3#u0;A zo|;RT!>lbG?YEhODIW=HmFE}1_di(N7to~abs+pY?W%@KKr=e=w&I7aanw>R!Vz+< zJr;{c0l##$zX-hHK<}Cc??M!n*_`*+N;X2IZvsv}5_A^~RON`FhS~STZzVg$htTjQ z8yb=X=}xa$2f&lAz>M4UqtJL3rI#Id zCD`QsML6^msL_@a4A4>`ErdGYY@o@|>jTRO7)F2k-^_2XQ4n-YA=ctf4mM1kdnt@qyi_7}){3+it(u%FFj0k7bbxGj4fbiS6s(T&Ke z&4C+-{HE|d^_oepf`+l7GG$-`TZ2mLSF9O?3lFO?l@(i^7NbB)6WzkV``t~cj5@A( z9^?#!$E90;C4)0lim2KEE*_|CF`BMq_EX?WRn76l;GBOunV{D8_OUyZeY?NR`nChwR3)ACGc5w)j|hq)l7Lf)@oDfD~f*Qdy{B(|*gOpFm$oR&9Q zeIZZALdbqs2z*s8{wm0Rle!{WFEmMNa3RvaICF zmFdB~R&^?z3uc>piGrJRi zN1NYB7l?X3cUxgiPM4a-iDhD$ix`J$SR<%G(LqYZG_JuX2eAXxNua-Y5)_R}x75=f z@g*vnw0l`O^72g=^*-tXkJ5>gtd~%Ws<5~Q6LR$`Ais;~5O+=VC1hAC#|LA>>ZQX* zomJIX9pWA8sI}F#V!i%dblc+Xgb<90oFop~m~z3lP+i^IG}}%9Yn|ke8JvOxJ|O)} zvw0XSnBLzTlY;HwqCFKv>BTx`0I_aMvdPLI%BgC-B6go8EEuvDuW5>3`5t3bs&bKT zLPHkrkpfZQ*eBZR^(BX=d2@J_qI(eD607`5H_Thh(5b7TX8;BRN-&x2KE=FVN+ozB zWl0;m+N=nfxL2FMCY(x8L-_W6U;~B+@#hV-Sk*@%dA{TMnF?aSL4!0576mcka48Z1 zZ%Murx(rzi6z4AcfkI!)DE%?4VRXuyZV=09cSje!F{PSJtX7UV>$LZJGsOWD?)ilXMP7$`+MDhX8*Z$tAra5G2wcul()j_}(Bblouf* znFuzYySX9U3=02NlyT0tD*a0d2ZwTi9K4zfXRWH;IiT6$S3}Z%-p5j$MP0YN_jdX? z2Bn*8K)CWzL}|wU1^4SC6(GR0kjD=~v?dCMDG<8Kj$0aDuUnk{Pn!t7GY=_zSU;Tj z&CMoP@s1-7THrpH{z4r+JTNnSYh#p>f_Smu;e@LtN{8kLMyhXhLaAZqSq9a^{(!nJ z9I5>a&nN^8uK$M8mb@0iNL!3!nYflXvD4#Q1y`{nb3Tn?K7e)eo<<)cU1lfa>X`CD z1<>7baYbRy2qg?SC}kj5sK@^R%p~)7VIATI|+@1sV#j+M25z}A3$ied= zpWaytdL{i_Xdwl@g>69^O88`~^9<9es{Cc!-oTR_L>`?HNoi-@kk~N=+hd26o?vX* zNkhxyk+Au67pxaNn`BExg4m72w-(`VPt{lwA>53MGdFZjSITFbDhaj?rY|3jWg0F=5*<67CK zSq{4-SbdOj|E#9r6&dwTL$r~LaQO(4hKyKOuF<+(|BxR{G291O^q*SYb7aPcei@X) zXfn1p=-z`r7Yd!jrXA!=Trk;&(9%G2uYqQ%*-Hn-UbEo%! z!e*9Es64WQUg3RaZ(JO^?*cDfxd|<{rQ*N)L-8P46fa~y!D<*!m3eOCO1Cvs4D{9p zbuhbE1kWq+qgSQp#MPZbz?>V=Ot5Ck8@7Rfp&}|!q3sk`2wMAOS&{teX9G$&iQANuLYrtpu+rgqVB6#)IIA~$;JUyN_TXo8RE&uy z(!f0!Kk3R7+u-0MBkwvHP20}q67={;q2~_f_un;FXE${B;hetu1{>vjAi;gc_I zbakN~!pVwkV#BMPA&0K{b$|*$)>2fbS>w*vuY=~vgH`vFcbl+&EGu;>D0Dq~B3BHER{wV=H z7sOJDO)U;%eTBZ9exXhJJBB@X{cjOUG^PYZn0HHeAcl=Ih!_qGBaSuXmWHdjAD(@B zw6^M<@uRQrHYOON^m>j>F-L@g>`GpU=SP4LLPs;NFb?@rQ2$)U1j$~r?N9=SB77ph z^zfrHSRJvT?lYke!iqbjTPKo*g6p8#X%6qSXr;6;6i1g^T6a;5QaQbS z?MVO%232-lgcqMBRvoDUT$9J~?d)VJ#xS~+YCaS8ds2FnO1e5KG90JtFh$j5m)j_{%E*nfH+?5!WGi^C{{rg5Hg-N`>P!2JX5w9&Bw5+?VbLj+DZRi zZ@&qMjxcu+Mi{>U9}ap}GH0Wghj6+2$yso(ETy}#W_6dHri_w7K4Q~ym>Llbd~2!X zBi691HA{4pVw+*N?M=q!7!30)xf4DV%0rP1iAmC&?~u5_Wa+S z?tkq0KRAf`wf!%7fd92(7_u*6LR$`7dMs9S^P|kzf7?JgEOQT>qbVa5vqJ z9RNVe|6e?)18tAGoRMl|)^(jp>>4lATazw)%-f4eJ2UFDzM`HOfN76RFiNkHr7URp zB|=|Q>^k0|8$H89Vj-F-#9Wjhij60}0g)Qx5FRs%EDbefSZjoXyPqIPTXB2iX9w>R z_Yz+P;(S;6B3XUn_i_2r{rK_K+Pw-3?+P)xjzt(eXQOoQ3e%93n{8edlgC{eaBqO& z*Z>G4w;~_vwZNz|)j-I_@%tmP$KC*~z;))av2I)=mOm&~m2alnO%9QmtA9?ZDr!!t zcJdyB#@S60mIpsASEq>c8i+5F-gdMhPnlUJC~EL<{iAqen>QV0My6rQxh9QXEE9{~ zN{qv<+27yOX9QZ2pT?P%wdSw&eO$txm^q5usJYPDxVRLv(K4Gg;Fe;g<5cG92TvZM zw=|nioMSm+9zdR|wt36iq<5!-mfsxwqtbd~(ksK4-(0+KkdM4B%8op&RyswwvMT*! zUb9wRgjbJDSC*?R*z#&H zc+-Axq1hZdtjIF_{3Qz?hb|OnqntRV*X86}^uFod5iqz4mp8mB@MjS$#J7 zpzf9O0GV)Db*$6SQOCBbP-D?CaCWY?j)|g8mGf4F}JtI{tl7@~2s?z8AFi7|KyXS z11w#gHdKJ6Dqjl=Cxu-8`M5)F$l)FJ{p-nzI#c@k)>g20>UvDlKw3rr~M7u z>v;u9q~l~t@-it=LB((M!O=if2W`9=o9pTiW?x14iB?N7-3_O=db;lVC6YYO!r%F# zS1Kp#q|KI#6{4lh^>Hu(eV)6=3%QShj|}0No3`f>UEaY0lgEKSbJ?L4}dP9zcehfX$GVk@;h83$ffInZ0{n0?aen*5Rd$3mGo=4>!f zSeCus-%2s|AAH>Wtd0wVh|gd4G*VUURPNiVRtHn%VHY?be*CQ7`g3OTs6;7WFF?t2 z)90~-x5+%Q}kwiVq6V0haH`?+GEO%jQtc z3ve@$rf@XC$qwpc2X+?g1e(86R+F7n*f^b>OFVZ zwvNsc-P@+1*7Rc&ddd0n+v{Q&#wu5y_rTQK^LNPRYpe$wzgNuH!K^QFbLnqq8%Hw^ zk3FBc`>jx6F@El8u}V~;+oOu+qZ9KYU=L< zvbU?U&Tr;qkQM!Ke7ZPzSH5UBV)u-w^PLpsD5>#c#kgNZ_qvskLhbdKK(3Ej$a5J% zyOksJkhms~Ay<5Vq?un4xI{o>1c>tIW9NI}XnJdts4ShoF4}B2vv!2g^UMaw+ZK_x z$J`wvczaEvZgvPxxf~)9o0xVW>2be!9rSCm z7#85rzGd$Vm8|+CdCz7)S6?m;e|K^J=eCb8z2#AR7}v5J=}-9-so0c+MK@)8G4ykiUqkbiuUboF->%RM$R%QaYBz^qz3Ed?B)M0F>3cG z9_sJznOFCWJlfW(r@3CZcm7?q5h#q7Tr*2;`WT+D`RFksVE*^LnH)lRYaFM~`AM^A zsx6g~RhH%IsvLvQ4x)3c=0^LWChNMT{`C2S;}+<1?(62K(RFDKsUQ)C!2qoPmCl_H ztNyO{f35|LDG>Ypc(XH>B^1Ox$! z)fk{^&ce1{Vq@5c-?bkCd{nU&V^J9f{FM>(A#rT-DROg7SHPo@<#LWWGYs7wz=fE@eLB!)`&BbwB=9j8^in4ORM8uML&Dol4MWGb-2CkmY;< zNc4LcPVDCiu~eCXy`u_~R3*KMmDcHs^ca#NAmmKcIsGJd+lDIP7<9&Z3;VbFhbYMV z(8d}yYYL~K^T@LdeX1puiIN2uE3F*nQjWcBP)tt8%}F@Q_KjZq%A9AG9^e%6ChHX} z-I5%s+z1~Dxb2%euJ-wT7wq`u&FMkDC)5qVjzNV^taPzA$Do zKv-2CKz7pxNFfGM9)Ua&09hDGgOOi~^X7|egX;I?78ykQquy}=Vh7fPcd_i*f{rLot9xTGq}GN zecmx?E2;g>8a{@xm@X;{um#rSG*N9QklPyvoHqep>aMROM7C}d%1sy&>n2Msxx>QSmw=s!dk}%`A z2H$rUuP+j$qb^hJS2B;9p@yb?_8O>IzNaZ8dpbUWwH83&+)zSv*a5urp4A;<#8>w8h zy9)4q9};{71va0b7o!)8-2dCxg&=OtS0Vk{!c`d*{Am;n?XNK)EEN&#-d0d53;@3o z0=k0&%i|G#lL0CJ_c`vl0Eq^G|9iWLB$znFpsI`WEs9pQUY9l#Ga_vu?_%(W!nmH z+}G0r0Zv2j(e1jUqqkU;_vL}Fr>ElO@(&BmAPU;@(R0T_MUw<#DAu#)A=k4Lug3l6 zO@E0O958~z7!SV=sMtsRIY4=cbt#EmZ;0cd3*>Y~lt^y+WpP{kY7rgBMM~Q1BLp&_ zB?-=LlYw?p5iZV4#W3{Z^HH|;WzC6joP4CJpMqFt2(DhwtV7S3mc$F=fG@jJw$za2h5q-Opg{$A6${|(Vz z`o<|`CTOg;)ftQ0W6K zeyl-@{oJH1o@Y5wkI6{_jk+m1Eb)P*DxMsiRK%W#jW{&ay-k|curzK>m+sGwUAqJN z%LlE&?+!BY;TG`fH+I*Ub`Mh%>Dp%Jg?nU~CIF}iyE`GVc*fM6^Y5MhR^*eU>@ukD(>rvZv>x{74|8*riCSSrFabR0Z?N;~Ea z1QxNG+gAfs{CW=qX}x(7SjKgP*5=kT_Bq@JtFTBpvOD z3J%plV;EcUG!N}jY5GkE@HuU_;q4_Y{X=KPE^J*c-5%+8VwEuQ3H2k(aKIl||65W8)UZxo~9rq+I%n139 z|HLl?dMNkm*6ppxNWtu)U7Z!y|2a{fy1`nz0gPS#-!LRn&t2xt zydUoQnsqv@P6}PNEd@n4*gtT-BT=_RXZ^4MzQ;UkN{$5<=h-w*uY8O*oDAWqnW~|? zOtM~_@B4weuPCg3RgNDc`;mu9J4q~CrnS-ZD_2=`OsM{8TCh}Zp~S2%!}>}F(Z9!Q(}>&3$Wb61Dt=xCpKV(O z{~`rK^wv(#dUWf@0WaThw|a8${R8E2{SbMJ`loJVtEz_kKpYaFWqT)W9%TVyEu!@g zkg%e{pO`MoI&NYy1biXQWyr`-5k|Ps*|7FN+unq{N-M>DYR&_{NNl!CD}~uvlp=EJ zCr*Pz%ocD&7QnZmOVQWlx_8bafVMvh9~+y9PM9MK!QRW>M%pmDEwsIZPWbXaILQAX z5iGoetC5jEp}c+;qhH|Wzm%5#Wo%dP!2tl;zdF^|r~VUC`WHO(FM-^49FhJ14@iU) z@PCj9(p_0)4So?&U{XPpOq#?A4ITPZQ5jJ_5H*3AupUSs38fA~CW%x*EjG4IN?S-zcso5Pp&CTiQb^Ca>Vq5c;Bwp*7E)u! z(F4aTI4o^^{neQC4oaEq{&8CRA}e`!Yz0NwFQ$WMU`B9qH2(0Q32sUO z47n8ikh~gCGdY*FhWnA6?0{5<>`f}Os1;

+h<_@3f#BMR@7*F*(># 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 GK>r_nOJ{rl diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml new file mode 100644 index 0000000..8542005 --- /dev/null +++ b/android/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/index.js b/index.js index 4311031..92d79dc 100644 --- a/index.js +++ b/index.js @@ -3,14 +3,22 @@ import { NativeModules } from 'react-native'; const RNCloudPaymentsModule = NativeModules.RNCloudPayments; export default class RNCloudPayments { - static async isValidCard(cardNumber, cardExp, cardCvv) { + static async isValidNumber(cardNumber) { try { - return await RNCloudPaymentsModule.isValidNumber(cardNumber, cardExp, cardCvv); + return await RNCloudPaymentsModule.isValidNumber(cardNumber); } catch(error) { return createError(error); } } + static async isValidExpired(cardExp) { + try { + return await RNCloudPaymentsModule.isValidExpired(cardExp); + } catch(error) { + return createError(error); + } + } + static async getType(cardNumber, cardExp, cardCvv) { try { return await RNCloudPaymentsModule.getType(cardNumber, cardExp, cardCvv); @@ -26,6 +34,14 @@ export default class RNCloudPayments { return createError(error); } } + + static async show3DS(acsUrl, paReq, transactionId) { + try { + return await RNCloudPaymentsModule.show3DS(acsUrl, paReq, transactionId); + } catch(error) { + return createError(error); + } + } } class RNCloudPaymentsError extends Error { diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index 20e27be..7e32080 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -6,8 +6,6 @@ @implementation RNCloudPayments RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(isValidNumber: (NSString *)cardNumber - cardExp: (NSString *)cardExp - cardCvv: (NSString *)cardCvv resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject) { @@ -18,6 +16,17 @@ @implementation RNCloudPayments } }; +RCT_EXPORT_METHOD(isValidExpired: (NSString *)cardExp + resolve: (RCTPromiseResolveBlock)resolve + reject: (RCTPromiseRejectBlock)reject) +{ + if([Card isExpiredValid: cardExp]) { + resolve(@YES); + } else { + resolve(@NO); + } +}; + RCT_EXPORT_METHOD(getType: (NSString *)cardNumber cardExp: (NSString *)cardExp cardCvv: (NSString *)cardCvv diff --git a/ios/SDK/Card.h b/ios/SDK/Card.h index 659a3b7..c2dfcbb 100755 --- a/ios/SDK/Card.h +++ b/ios/SDK/Card.h @@ -15,6 +15,8 @@ typedef enum { +(BOOL) isCardNumberValid: (NSString *) cardNumberString; ++(BOOL) isExpiredValid: (NSString *) expiredString; + /** * Create cryptogram * cardNumberString valid card number stirng diff --git a/ios/SDK/Card.m b/ios/SDK/Card.m index b465c13..fcf6ec3 100755 --- a/ios/SDK/Card.m +++ b/ios/SDK/Card.m @@ -127,6 +127,23 @@ +(BOOL) isCardNumberValid: (NSString *) cardNumberString { return ((oddSum + evenSum) % 10 == 0); } ++(BOOL) isExpiredValid: (NSString *) expiredString { + NSArray *cardDateComponents = [expiredString componentsSeparatedByString:@"/"]; + + if ([cardDateComponents[0] integerValue] > 12) { + return NO; + } + + NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitYear fromDate:[NSDate date]]; + int shortYear = [components year] % 100; + + if ([cardDateComponents[1] integerValue] < shortYear) { + return NO; + } + + return YES; +} + -(NSString *) makeCardCryptogramPacket: (NSString *) cardNumberString andExpDate: (NSString *) expDateString andCVV: (NSString *) CVVString andMerchantPublicID: (NSString *) merchantPublicIDString { // ExpDate must be in YYMM format From 711d7904f372eec839085f41bd3190d5aff03475 Mon Sep 17 00:00:00 2001 From: Mikhail Yarmoluk Date: Thu, 30 May 2019 15:35:31 +0300 Subject: [PATCH 02/15] Fix README.md, show3DS --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e5fe202..c8b4f50 100644 --- a/README.md +++ b/README.md @@ -123,18 +123,15 @@ __Arguments__ - `url` - `String` Url redirect. - `transactionId` - `String` Transaction ID. - `token` - `String` Token. -- `successCallback` - `function` Call when result is success. -- `errorCallback` - `function` Call when result is error. __Examples__ ```js import RNCloudPayment from 'react-native-cloudpayments'; -RNCloudPayment.show3DS('https://demo.cloudpayments.ru', '1237618734', '....1d3d22r..', result => { - console.log(result) -}, error => { - console.log(error) -}) +RNCloudPayment.show3DS('https://demo.cloudpayments.ru', '1237618734', '....1d3d22r..') + .then(result => { + console.log(result); + }); ``` # License From 784d63aba55befe7e6c7c908c7f8c8676870c521 Mon Sep 17 00:00:00 2001 From: Mikhail Yarmoluk Date: Thu, 30 May 2019 15:59:58 +0300 Subject: [PATCH 03/15] Fix android check card number and expired --- .../src/main/java/com/rncloudpayments/CloudPayments.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/rncloudpayments/CloudPayments.java b/android/src/main/java/com/rncloudpayments/CloudPayments.java index 99b5197..855ebb0 100644 --- a/android/src/main/java/com/rncloudpayments/CloudPayments.java +++ b/android/src/main/java/com/rncloudpayments/CloudPayments.java @@ -22,7 +22,8 @@ public String getName() { @ReactMethod public void isValidNumber(String cardNumber, Promise promise) { try { - boolean numberStatus = CPCard.isValidNumber(cardNumber); + String validFormatNumber = cardNumber.replace(" ", ""); + boolean numberStatus = CPCard.isValidNumber(validFormatNumber); promise.resolve(numberStatus); } catch (Exception e) { @@ -33,7 +34,8 @@ public void isValidNumber(String cardNumber, Promise promise) { @ReactMethod public void isValidExpired(String cardExpired, Promise promise) { try { - boolean expiredStatus = CPCard.isValidExpDate(cardExpired); + String validFormatExp = cardExpired.replace("/", ""); + boolean expiredStatus = CPCard.isValidExpDate(validFormatExp); promise.resolve(expiredStatus); } catch (Exception e) { From 37597e84a6b92344e8cbde492f5becf9983406a6 Mon Sep 17 00:00:00 2001 From: Dmitry Sytsevich Date: Thu, 30 May 2019 16:48:20 +0300 Subject: [PATCH 04/15] SDWebViewController --- ios/RNCloudPayments.xcodeproj/project.pbxproj | 22 +- ios/SDWebViewController/SDWebViewController.h | 18 ++ ios/SDWebViewController/SDWebViewController.m | 253 ++++++++++++++++++ ios/SDWebViewController/SDWebViewDelegate.h | 17 ++ 4 files changed, 307 insertions(+), 3 deletions(-) create mode 100755 ios/SDWebViewController/SDWebViewController.h create mode 100755 ios/SDWebViewController/SDWebViewController.m create mode 100755 ios/SDWebViewController/SDWebViewDelegate.h diff --git a/ios/RNCloudPayments.xcodeproj/project.pbxproj b/ios/RNCloudPayments.xcodeproj/project.pbxproj index 6993c14..e861012 100644 --- a/ios/RNCloudPayments.xcodeproj/project.pbxproj +++ b/ios/RNCloudPayments.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 954FC90C20AADA700017B273 /* RNCloudPayments.m in Sources */ = {isa = PBXBuildFile; fileRef = 954FC90B20AADA700017B273 /* RNCloudPayments.m */; }; 954FC9F920AC08E60017B273 /* Card.m in Sources */ = {isa = PBXBuildFile; fileRef = 954FC9F620AC08E60017B273 /* Card.m */; }; 954FC9FA20AC08E60017B273 /* NSDataENBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 954FC9F720AC08E60017B273 /* NSDataENBase64.m */; }; + AE8EA39622A015910033E067 /* SDWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AE8EA39522A015910033E067 /* SDWebViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -32,6 +33,9 @@ 954FC9F620AC08E60017B273 /* Card.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Card.m; sourceTree = ""; }; 954FC9F720AC08E60017B273 /* NSDataENBase64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSDataENBase64.m; sourceTree = ""; }; 954FC9F820AC08E60017B273 /* Card.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Card.h; sourceTree = ""; }; + AE8EA39322A015910033E067 /* SDWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebViewController.h; sourceTree = ""; }; + AE8EA39422A015910033E067 /* SDWebViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebViewDelegate.h; sourceTree = ""; }; + AE8EA39522A015910033E067 /* SDWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebViewController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -48,10 +52,11 @@ 954FC8FE20AADA700017B273 = { isa = PBXGroup; children = ( - 954FC9F420AC08D70017B273 /* SDK */, - 954FC90B20AADA700017B273 /* RNCloudPayments.m */, - 954FC90A20AADA700017B273 /* RNCloudPayments.h */, 954FC90820AADA700017B273 /* Products */, + 954FC90A20AADA700017B273 /* RNCloudPayments.h */, + 954FC90B20AADA700017B273 /* RNCloudPayments.m */, + 954FC9F420AC08D70017B273 /* SDK */, + AE8EA39222A015910033E067 /* SDWebViewController */, ); sourceTree = ""; }; @@ -74,6 +79,16 @@ path = SDK; sourceTree = ""; }; + AE8EA39222A015910033E067 /* SDWebViewController */ = { + isa = PBXGroup; + children = ( + AE8EA39322A015910033E067 /* SDWebViewController.h */, + AE8EA39422A015910033E067 /* SDWebViewDelegate.h */, + AE8EA39522A015910033E067 /* SDWebViewController.m */, + ); + path = SDWebViewController; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -129,6 +144,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + AE8EA39622A015910033E067 /* SDWebViewController.m in Sources */, 954FC90C20AADA700017B273 /* RNCloudPayments.m in Sources */, 954FC9F920AC08E60017B273 /* Card.m in Sources */, 954FC9FA20AC08E60017B273 /* NSDataENBase64.m in Sources */, diff --git a/ios/SDWebViewController/SDWebViewController.h b/ios/SDWebViewController/SDWebViewController.h new file mode 100755 index 0000000..f59d62e --- /dev/null +++ b/ios/SDWebViewController/SDWebViewController.h @@ -0,0 +1,18 @@ +// +// SDWebViewController.h +// SDWebViewController +// +// Created by Dmitry Sytsevich on 5/30/19. +// Copyright © 2019 Dmitry Sytsevich. All rights reserved. +// + +#import + +@protocol SDWebViewDelegate; +@interface SDWebViewController : UIViewController + +@property (weak, nonatomic) id m_delegate; + +- (id)initWithURL:(id)url login:(NSString *)login password:(NSString *)password; + +@end diff --git a/ios/SDWebViewController/SDWebViewController.m b/ios/SDWebViewController/SDWebViewController.m new file mode 100755 index 0000000..8eac37a --- /dev/null +++ b/ios/SDWebViewController/SDWebViewController.m @@ -0,0 +1,253 @@ +// +// SDWebViewController.m +// SDWebViewController +// +// Created by Dmitry Sytsevich on 5/30/19. +// Copyright © 2019 Dmitry Sytsevich. All rights reserved. +// + +#define IBT_BGCOLOR [UIColor whiteColor] +#define IBT_ADDRESS_TEXT_COLOR [UIColor colorWithRed:.44 green:.45 blue:.46 alpha:1] +#define IBT_PROGRESS_COLOR [UIColor colorWithRed:0 green:.071 blue:.75 alpha:1] + +#import "SDWebViewController.h" +#import "SDWebViewDelegate.h" + +@interface SDWebViewController () +< + UIWebViewDelegate +> +{ + // address bar + UIImageView *m_addressBarView; + UILabel *m_addressLabel; + + // URL + NSURL *m_currentUrl; + + BOOL m_bAutoSetTitle; +} +@property (strong, nonatomic) UIWebView *m_webView; + +@property (strong, nonatomic) NSString *m_initUrl; +@property (strong, nonatomic) NSString *m_login; +@property (strong, nonatomic) NSString *m_password; +@property (strong, nonatomic) NSMutableDictionary *m_extraInfo; + +- (void)initWebView; +- (void)initAddressBarView; +- (void)removeAddressBar; +- (void)initNavigationBarItem; + +@end + +@implementation SDWebViewController + +#pragma mark - + +- (id)initWithURL:(id)url login:(NSString *)login password:(NSString *)password { + self = [super init]; + if (!self) { + return nil; + } + + self.m_initUrl = url; + self.m_login = login; + self.m_password = password; + + + if ([url isKindOfClass:[NSString class]]) { + self.m_initUrl = url; + } + else if ([url isKindOfClass:[NSURL class]]) { + self.m_initUrl = [NSString stringWithFormat:@"%@", url]; + } + + m_bAutoSetTitle = YES; + + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = IBT_BGCOLOR; + + if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { + self.edgesForExtendedLayout = UIRectEdgeNone; + } + + [self initNavigationBarItem]; + [self initAddressBarView]; + [self initWebView]; + + [self goToURL:self.m_initUrl login:self.m_login password:self.m_password]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)dealloc +{ + [self.m_webView stopLoading]; + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + self.m_webView.delegate = nil; + + m_addressBarView = nil; + m_addressLabel = nil; + + m_currentUrl = nil; +} + +#pragma mark - Private Method + +- (void)initWebView { + self.m_webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; + self.m_webView.backgroundColor = [UIColor clearColor]; + self.m_webView.delegate = self; + self.m_webView.scalesPageToFit = YES; + self.m_webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self.view addSubview:self.m_webView]; +} + +- (void)updateDisplayTitle:(NSString *)nsTitle { + self.title = nsTitle; +} + +#pragma mark - Address Bar + +- (NSString *)getAddressBarHostText:(NSURL *)url { + if ([url.host length] > 0) { + return [NSString stringWithFormat:NSLocalizedString(@"Provided by %@", nil), url.host]; + } + else { + return @""; + } +} + +- (void)initAddressBarView { + if (!m_addressBarView) { + m_addressBarView = [[UIImageView alloc] init]; + m_addressBarView.frame = (CGRect){ + .origin.x = 0, + .origin.y = 0, + .size.width = CGRectGetWidth(self.view.bounds), + .size.height = 40 + }; + + m_addressLabel = [[UILabel alloc] init]; + m_addressLabel.frame = CGRectInset(m_addressBarView.bounds, 10, 6); + m_addressLabel.textColor = [UIColor clearColor]; + m_addressLabel.textAlignment = NSTextAlignmentCenter; + m_addressLabel.textColor = IBT_ADDRESS_TEXT_COLOR; + m_addressLabel.font = [UIFont systemFontOfSize:12]; + + [m_addressBarView addSubview:m_addressLabel]; + } + + [self.view addSubview:m_addressBarView]; +} + +- (void)removeAddressBar { + [m_addressBarView removeFromSuperview]; + m_addressBarView = nil; + m_addressLabel = nil; +} + +#pragma mark - Navigation Bar + +- (void)initNavigationBarItem { + UIBarButtonItem *backItem = + [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Done", nil) + style:UIBarButtonItemStylePlain + target:self + action:@selector(onCloseAction:)]; + + self.navigationItem.rightBarButtonItems = @[ backItem ]; + +} + +- (void)onCloseAction:(__unused id)sender { + [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; +} + +#pragma mark - WebView Action + +- (BOOL)isTopLevelNavigation:(NSURLRequest *)req { + if (req.mainDocumentURL) { + return [req.URL isEqual:req.mainDocumentURL]; + } + else { + return YES; + } +} + +- (void)goToURL:(NSString *)url login:(NSString *)login password:(NSString *)password { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; + NSString *post = [NSString stringWithFormat:@"login=%@&password=%@", login, password]; + NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postData]; + + [self.m_webView loadRequest:request]; +} + +#pragma mark UIWebViewDelegate + +- (BOOL)webView:(UIWebView *)webView +shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType +{ + m_currentUrl = request.mainDocumentURL; + m_addressLabel.text = [self getAddressBarHostText:m_currentUrl]; + + return YES; +} + +- (void)webViewDidStartLoad:(UIWebView *)webView { + if ([_m_delegate respondsToSelector:@selector(onWebViewDidStartLoad:)]) { + [_m_delegate onWebViewDidStartLoad:webView]; + } + + if ([self isTopLevelNavigation:webView.request]) { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + } + + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView { + if ([_m_delegate respondsToSelector:@selector(onWebViewDidFinishLoad:)]) { + [_m_delegate onWebViewDidFinishLoad:webView]; + } + + if ([self isTopLevelNavigation:webView.request]) { + m_currentUrl = webView.request.mainDocumentURL; + m_addressLabel.text = [self getAddressBarHostText:m_currentUrl]; + + // get title + if (m_bAutoSetTitle) { + NSString *nsTitle = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; + [self updateDisplayTitle:nsTitle]; + } + } + + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + if ([_m_delegate respondsToSelector:@selector(webViewFailToLoad:)]) { + [_m_delegate webViewFailToLoad:error]; + } + + if ([error code] != NSURLErrorCancelled && + [self isTopLevelNavigation:webView.request]) { + + } + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; +} + + +@end diff --git a/ios/SDWebViewController/SDWebViewDelegate.h b/ios/SDWebViewController/SDWebViewDelegate.h new file mode 100755 index 0000000..e65ddd5 --- /dev/null +++ b/ios/SDWebViewController/SDWebViewDelegate.h @@ -0,0 +1,17 @@ +// +// SDWebViewDelegate.h +// SDWebViewController +// +// Created by Dmitry Sytsevich on 5/30/19. +// Copyright © 2019 Dmitry Sytsevich. All rights reserved. +// + +#import + +@protocol SDWebViewDelegate +@optional +- (void)onWebViewWillClose:(UIWebView *)webView; +- (void)onWebViewDidFinishLoad:(UIWebView *)webView; +- (void)onWebViewDidStartLoad:(UIWebView *)webView; +- (void)webViewFailToLoad:(NSError *)error; +@end From 965bc1f01a5eecbb5986b75f8afc94f755551526 Mon Sep 17 00:00:00 2001 From: Mikhail Yarmoluk Date: Thu, 30 May 2019 17:04:43 +0300 Subject: [PATCH 05/15] Change arguments name. Add term url --- ios/RNCloudPayments.m | 9 ++++ ios/SDWebViewController/SDWebViewController.h | 2 +- ios/SDWebViewController/SDWebViewController.m | 52 +++++++++---------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index 7e32080..3f5deb8 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -52,4 +52,13 @@ @implementation RNCloudPayments resolve(cryptogram); } + +RCT_EXPORT_METHOD(show3DS: (NSString *)url + transactionId: (NSString *)transactionId + token: (NSString *)token + resolve: (RCTPromiseResolveBlock)resolve + reject: (RCTPromiseRejectBlock)reject) +{ + +} @end diff --git a/ios/SDWebViewController/SDWebViewController.h b/ios/SDWebViewController/SDWebViewController.h index f59d62e..e57f956 100755 --- a/ios/SDWebViewController/SDWebViewController.h +++ b/ios/SDWebViewController/SDWebViewController.h @@ -13,6 +13,6 @@ @property (weak, nonatomic) id m_delegate; -- (id)initWithURL:(id)url login:(NSString *)login password:(NSString *)password; +- (id)initWithURL:(id)url transactionId:(NSString *)transactionId token:(NSString *)token; @end diff --git a/ios/SDWebViewController/SDWebViewController.m b/ios/SDWebViewController/SDWebViewController.m index 8eac37a..bebacbf 100755 --- a/ios/SDWebViewController/SDWebViewController.m +++ b/ios/SDWebViewController/SDWebViewController.m @@ -9,7 +9,7 @@ #define IBT_BGCOLOR [UIColor whiteColor] #define IBT_ADDRESS_TEXT_COLOR [UIColor colorWithRed:.44 green:.45 blue:.46 alpha:1] #define IBT_PROGRESS_COLOR [UIColor colorWithRed:0 green:.071 blue:.75 alpha:1] - +#define POST_BACK_URL "https://demo.cloudpayments.ru/WebFormPost/GetWebViewData" #import "SDWebViewController.h" #import "SDWebViewDelegate.h" @@ -21,17 +21,17 @@ @interface SDWebViewController () // address bar UIImageView *m_addressBarView; UILabel *m_addressLabel; - + // URL NSURL *m_currentUrl; - + BOOL m_bAutoSetTitle; } @property (strong, nonatomic) UIWebView *m_webView; @property (strong, nonatomic) NSString *m_initUrl; -@property (strong, nonatomic) NSString *m_login; -@property (strong, nonatomic) NSString *m_password; +@property (strong, nonatomic) NSString *m_transactionId; +@property (strong, nonatomic) NSString *m_token; @property (strong, nonatomic) NSMutableDictionary *m_extraInfo; - (void)initWebView; @@ -45,43 +45,43 @@ @implementation SDWebViewController #pragma mark - -- (id)initWithURL:(id)url login:(NSString *)login password:(NSString *)password { +- (id)initWithURL:(id)url transactionId:(NSString *)transactionId token:(NSString *)token { self = [super init]; if (!self) { return nil; } - + self.m_initUrl = url; - self.m_login = login; - self.m_password = password; - - + self.m_transactionId = transactionId; + self.m_token = token; + + if ([url isKindOfClass:[NSString class]]) { self.m_initUrl = url; } else if ([url isKindOfClass:[NSURL class]]) { self.m_initUrl = [NSString stringWithFormat:@"%@", url]; } - + m_bAutoSetTitle = YES; - + return self; } - (void)viewDidLoad { [super viewDidLoad]; - + self.view.backgroundColor = IBT_BGCOLOR; - + if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { self.edgesForExtendedLayout = UIRectEdgeNone; } - + [self initNavigationBarItem]; [self initAddressBarView]; [self initWebView]; - - [self goToURL:self.m_initUrl login:self.m_login password:self.m_password]; + + [self goToURL:self.m_initUrl login:self.m_transactionId password:self.m_token]; } - (void)didReceiveMemoryWarning { @@ -94,7 +94,7 @@ - (void)dealloc [self.m_webView stopLoading]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; self.m_webView.delegate = nil; - + m_addressBarView = nil; m_addressLabel = nil; @@ -136,17 +136,17 @@ - (void)initAddressBarView { .size.width = CGRectGetWidth(self.view.bounds), .size.height = 40 }; - + m_addressLabel = [[UILabel alloc] init]; m_addressLabel.frame = CGRectInset(m_addressBarView.bounds, 10, 6); m_addressLabel.textColor = [UIColor clearColor]; m_addressLabel.textAlignment = NSTextAlignmentCenter; m_addressLabel.textColor = IBT_ADDRESS_TEXT_COLOR; m_addressLabel.font = [UIFont systemFontOfSize:12]; - + [m_addressBarView addSubview:m_addressLabel]; } - + [self.view addSubview:m_addressBarView]; } @@ -164,9 +164,9 @@ - (void)initNavigationBarItem { style:UIBarButtonItemStylePlain target:self action:@selector(onCloseAction:)]; - + self.navigationItem.rightBarButtonItems = @[ backItem ]; - + } - (void)onCloseAction:(__unused id)sender { @@ -184,9 +184,9 @@ - (BOOL)isTopLevelNavigation:(NSURLRequest *)req { } } -- (void)goToURL:(NSString *)url login:(NSString *)login password:(NSString *)password { +- (void)goToURL:(NSString *)url transactionId:(NSString *)transactionId token:(NSString *)token { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; - NSString *post = [NSString stringWithFormat:@"login=%@&password=%@", login, password]; + NSString *post = [NSString stringWithFormat:@"MD=%@&PaReq=%@&TermUrl=%@", transactionId, token, POST_BACK_URL]; NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:postData]; From 84ea6cccf3e245701fb022d0d4f4ad979dbcde66 Mon Sep 17 00:00:00 2001 From: Dmitry Sytsevich Date: Thu, 30 May 2019 17:22:13 +0300 Subject: [PATCH 06/15] show3DS: openWebView; --- ios/RNCloudPayments.m | 27 ++++++++++++++++++- ios/RNCloudPayments.xcodeproj/project.pbxproj | 2 +- ios/SDWebViewController/SDWebViewController.m | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index 3f5deb8..cc216d4 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -1,7 +1,9 @@ #import "RNCloudPayments.h" #import "SDK/Card.m" +#import "SDWebViewController/SDWebViewController.h" +#import "SDWebViewController/SDWebViewDelegate.h" -@implementation RNCloudPayments +@implementation RNCloudPayments RCT_EXPORT_MODULE(); @@ -59,6 +61,29 @@ @implementation RNCloudPayments resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject) { + SDWebViewController *webViewController = [[SDWebViewController alloc] initWithURL:url transactionId:transactionId token:token]; + webViewController.m_delegate = self; + UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:webViewController]; + [[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:navigationController animated:YES completion:NULL]; +} + +#pragma MARK: - SDWebViewDelegate + +- (void)onWebViewWillClose:(UIWebView *)webView { + +} + +- (void)onWebViewDidFinishLoad:(UIWebView *)webView { + +} +- (void)onWebViewDidStartLoad:(UIWebView *)webView { + } + +- (void)webViewFailToLoad:(NSError *)error { + +} + + @end diff --git a/ios/RNCloudPayments.xcodeproj/project.pbxproj b/ios/RNCloudPayments.xcodeproj/project.pbxproj index e861012..14b3536 100644 --- a/ios/RNCloudPayments.xcodeproj/project.pbxproj +++ b/ios/RNCloudPayments.xcodeproj/project.pbxproj @@ -82,8 +82,8 @@ AE8EA39222A015910033E067 /* SDWebViewController */ = { isa = PBXGroup; children = ( - AE8EA39322A015910033E067 /* SDWebViewController.h */, AE8EA39422A015910033E067 /* SDWebViewDelegate.h */, + AE8EA39322A015910033E067 /* SDWebViewController.h */, AE8EA39522A015910033E067 /* SDWebViewController.m */, ); path = SDWebViewController; diff --git a/ios/SDWebViewController/SDWebViewController.m b/ios/SDWebViewController/SDWebViewController.m index bebacbf..019c8bd 100755 --- a/ios/SDWebViewController/SDWebViewController.m +++ b/ios/SDWebViewController/SDWebViewController.m @@ -81,7 +81,7 @@ - (void)viewDidLoad { [self initAddressBarView]; [self initWebView]; - [self goToURL:self.m_initUrl login:self.m_transactionId password:self.m_token]; + [self goToURL:self.m_initUrl transactionId:self.m_transactionId token:self.m_token]; } - (void)didReceiveMemoryWarning { From 6c4380ddc9891e8782259118a5980c06948f2a39 Mon Sep 17 00:00:00 2001 From: Mikhail Yarmoluk Date: Thu, 30 May 2019 18:09:08 +0300 Subject: [PATCH 07/15] Code cleanup --- .../src/main/java/com/rncloudpayments/CheckoutActivity.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/android/src/main/java/com/rncloudpayments/CheckoutActivity.java b/android/src/main/java/com/rncloudpayments/CheckoutActivity.java index 505d1e2..b1e1866 100644 --- a/android/src/main/java/com/rncloudpayments/CheckoutActivity.java +++ b/android/src/main/java/com/rncloudpayments/CheckoutActivity.java @@ -2,12 +2,9 @@ import android.app.Activity; import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.DialogInterface; import android.os.Bundle; import android.os.Handler; import android.view.View; -import android.view.Window; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.WritableMap; From d401c08c27839926a9fd306f732771e1a9ab3de9 Mon Sep 17 00:00:00 2001 From: Mikhail Yarmoluk Date: Tue, 4 Jun 2019 15:48:43 +0300 Subject: [PATCH 08/15] Fix ios deployment target version --- ios/RNCloudPayments.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/RNCloudPayments.xcodeproj/project.pbxproj b/ios/RNCloudPayments.xcodeproj/project.pbxproj index 14b3536..a0a489b 100644 --- a/ios/RNCloudPayments.xcodeproj/project.pbxproj +++ b/ios/RNCloudPayments.xcodeproj/project.pbxproj @@ -205,7 +205,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.3; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -257,7 +257,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.3; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; From 75ec00d30fecf1229e1cb15ece2bba8670e589f1 Mon Sep 17 00:00:00 2001 From: Dmitry Sytsevich Date: Thu, 30 May 2019 18:38:18 +0300 Subject: [PATCH 09/15] Bug fixes; --- ios/RNCloudPayments.h | 5 +++- ios/RNCloudPayments.m | 10 +++---- ios/SDWebViewController/SDWebViewController.m | 28 +++++++++---------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/ios/RNCloudPayments.h b/ios/RNCloudPayments.h index 3c88d5d..953a0f3 100644 --- a/ios/RNCloudPayments.h +++ b/ios/RNCloudPayments.h @@ -1,4 +1,7 @@ #import +#import "SDWebViewController.h" +#import "SDWebViewDelegate.h" + +@interface RNCloudPayments : NSObject -@interface RNCloudPayments : NSObject @end diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index cc216d4..c7c1279 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -1,9 +1,7 @@ #import "RNCloudPayments.h" #import "SDK/Card.m" -#import "SDWebViewController/SDWebViewController.h" -#import "SDWebViewController/SDWebViewDelegate.h" -@implementation RNCloudPayments +@implementation RNCloudPayments RCT_EXPORT_MODULE(); @@ -37,7 +35,7 @@ @implementation RNCloudPayments { CardType cardType = [Card cardTypeFromCardNumber: cardNumber]; NSString *cardTypeString = [Card cardTypeToString: cardType]; - + resolve(cardTypeString); } @@ -49,9 +47,9 @@ @implementation RNCloudPayments reject: (RCTPromiseRejectBlock)reject) { Card *_card = [[Card alloc] init]; - + NSString *cryptogram = [_card makeCardCryptogramPacket: cardNumber andExpDate:cardExp andCVV:cardCvv andMerchantPublicID:publicId]; - + resolve(cryptogram); } diff --git a/ios/SDWebViewController/SDWebViewController.m b/ios/SDWebViewController/SDWebViewController.m index 019c8bd..874db70 100755 --- a/ios/SDWebViewController/SDWebViewController.m +++ b/ios/SDWebViewController/SDWebViewController.m @@ -9,16 +9,15 @@ #define IBT_BGCOLOR [UIColor whiteColor] #define IBT_ADDRESS_TEXT_COLOR [UIColor colorWithRed:.44 green:.45 blue:.46 alpha:1] #define IBT_PROGRESS_COLOR [UIColor colorWithRed:0 green:.071 blue:.75 alpha:1] + #define POST_BACK_URL "https://demo.cloudpayments.ru/WebFormPost/GetWebViewData" + #import "SDWebViewController.h" #import "SDWebViewDelegate.h" -@interface SDWebViewController () -< - UIWebViewDelegate -> -{ - // address bar +@interface SDWebViewController () { + + // Address bar UIImageView *m_addressBarView; UILabel *m_addressLabel; @@ -81,7 +80,7 @@ - (void)viewDidLoad { [self initAddressBarView]; [self initWebView]; - [self goToURL:self.m_initUrl transactionId:self.m_transactionId token:self.m_token]; + [self loadURL:self.m_initUrl transactionId:self.m_transactionId token:self.m_token]; } - (void)didReceiveMemoryWarning { @@ -101,7 +100,7 @@ - (void)dealloc m_currentUrl = nil; } -#pragma mark - Private Method +#pragma MARK: - Private Method - (void)initWebView { self.m_webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; @@ -116,7 +115,7 @@ - (void)updateDisplayTitle:(NSString *)nsTitle { self.title = nsTitle; } -#pragma mark - Address Bar +#pragma MARK: - Address Bar - (NSString *)getAddressBarHostText:(NSURL *)url { if ([url.host length] > 0) { @@ -156,7 +155,7 @@ - (void)removeAddressBar { m_addressLabel = nil; } -#pragma mark - Navigation Bar +#pragma MARK: - Navigation Bar - (void)initNavigationBarItem { UIBarButtonItem *backItem = @@ -173,7 +172,7 @@ - (void)onCloseAction:(__unused id)sender { [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; } -#pragma mark - WebView Action +#pragma MARK: - WebView Action - (BOOL)isTopLevelNavigation:(NSURLRequest *)req { if (req.mainDocumentURL) { @@ -184,9 +183,9 @@ - (BOOL)isTopLevelNavigation:(NSURLRequest *)req { } } -- (void)goToURL:(NSString *)url transactionId:(NSString *)transactionId token:(NSString *)token { +- (void)loadURL:(NSString *)url transactionId:(NSString *)transactionId token:(NSString *)token { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; - NSString *post = [NSString stringWithFormat:@"MD=%@&PaReq=%@&TermUrl=%@", transactionId, token, POST_BACK_URL]; + NSString *post = [NSString stringWithFormat:@"MD=%@&PaReq=%@&TermUrl=%s", transactionId, token, POST_BACK_URL]; NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:postData]; @@ -194,7 +193,7 @@ - (void)goToURL:(NSString *)url transactionId:(NSString *)transactionId token:(N [self.m_webView loadRequest:request]; } -#pragma mark UIWebViewDelegate +#pragma MARK: - UIWebViewDelegate - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request @@ -249,5 +248,4 @@ - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; } - @end From adde36007fdee8015cf9295dfef555f8606fad24 Mon Sep 17 00:00:00 2001 From: Dmitry Sytsevich Date: Thu, 30 May 2019 22:52:37 +0300 Subject: [PATCH 10/15] Added shouldStartLoadWithRequest; --- ios/RNCloudPayments.m | 15 ++++++++ ios/SDWebViewController/SDWebViewController.m | 35 +++++++++---------- ios/SDWebViewController/SDWebViewDelegate.h | 3 ++ 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index c7c1279..4de0d05 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -1,8 +1,13 @@ #import "RNCloudPayments.h" #import "SDK/Card.m" +@interface RNCloudPayments () { + @property (nonatomic) RCTPromiseResolveBlock resolveWebView; +} + @implementation RNCloudPayments + RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(isValidNumber: (NSString *)cardNumber @@ -62,11 +67,21 @@ @implementation RNCloudPayments SDWebViewController *webViewController = [[SDWebViewController alloc] initWithURL:url transactionId:transactionId token:token]; webViewController.m_delegate = self; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:webViewController]; + [navigationController.navigationBar setTranslucent:false]; [[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:navigationController animated:YES completion:NULL]; } #pragma MARK: - SDWebViewDelegate +- (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType { + + // Detect url + NSString *url = request.URL; + NSString *sringUrl = request.URL.absoluteString; + + +} + - (void)onWebViewWillClose:(UIWebView *)webView { } diff --git a/ios/SDWebViewController/SDWebViewController.m b/ios/SDWebViewController/SDWebViewController.m index 874db70..30c7afd 100755 --- a/ios/SDWebViewController/SDWebViewController.m +++ b/ios/SDWebViewController/SDWebViewController.m @@ -10,7 +10,7 @@ #define IBT_ADDRESS_TEXT_COLOR [UIColor colorWithRed:.44 green:.45 blue:.46 alpha:1] #define IBT_PROGRESS_COLOR [UIColor colorWithRed:0 green:.071 blue:.75 alpha:1] -#define POST_BACK_URL "https://demo.cloudpayments.ru/WebFormPost/GetWebViewData" +#define POST_BACK_URL @"https://demo.cloudpayments.ru/WebFormPost/GetWebViewData" #import "SDWebViewController.h" #import "SDWebViewDelegate.h" @@ -88,8 +88,7 @@ - (void)didReceiveMemoryWarning { // Dispose of any resources that can be recreated. } -- (void)dealloc -{ +- (void)dealloc { [self.m_webView stopLoading]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; self.m_webView.delegate = nil; @@ -120,8 +119,7 @@ - (void)updateDisplayTitle:(NSString *)nsTitle { - (NSString *)getAddressBarHostText:(NSURL *)url { if ([url.host length] > 0) { return [NSString stringWithFormat:NSLocalizedString(@"Provided by %@", nil), url.host]; - } - else { + } else { return @""; } } @@ -177,15 +175,14 @@ - (void)onCloseAction:(__unused id)sender { - (BOOL)isTopLevelNavigation:(NSURLRequest *)req { if (req.mainDocumentURL) { return [req.URL isEqual:req.mainDocumentURL]; - } - else { + } else { return YES; } } - (void)loadURL:(NSString *)url transactionId:(NSString *)transactionId token:(NSString *)token { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; - NSString *post = [NSString stringWithFormat:@"MD=%@&PaReq=%@&TermUrl=%s", transactionId, token, POST_BACK_URL]; + NSString *post = [NSString stringWithFormat:@"MD=%@&PaReq=%@&TermUrl=%@", transactionId, token, POST_BACK_URL]; NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:postData]; @@ -195,10 +192,12 @@ - (void)loadURL:(NSString *)url transactionId:(NSString *)transactionId token:(N #pragma MARK: - UIWebViewDelegate -- (BOOL)webView:(UIWebView *)webView -shouldStartLoadWithRequest:(NSURLRequest *)request - navigationType:(UIWebViewNavigationType)navigationType -{ +- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { + + if ([_m_delegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { + [_m_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; + } + m_currentUrl = request.mainDocumentURL; m_addressLabel.text = [self getAddressBarHostText:m_currentUrl]; @@ -206,6 +205,8 @@ - (BOOL)webView:(UIWebView *)webView } - (void)webViewDidStartLoad:(UIWebView *)webView { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + if ([_m_delegate respondsToSelector:@selector(onWebViewDidStartLoad:)]) { [_m_delegate onWebViewDidStartLoad:webView]; } @@ -213,11 +214,11 @@ - (void)webViewDidStartLoad:(UIWebView *)webView { if ([self isTopLevelNavigation:webView.request]) { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; } - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; } - (void)webViewDidFinishLoad:(UIWebView *)webView { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + if ([_m_delegate respondsToSelector:@selector(onWebViewDidFinishLoad:)]) { [_m_delegate onWebViewDidFinishLoad:webView]; } @@ -226,17 +227,16 @@ - (void)webViewDidFinishLoad:(UIWebView *)webView { m_currentUrl = webView.request.mainDocumentURL; m_addressLabel.text = [self getAddressBarHostText:m_currentUrl]; - // get title if (m_bAutoSetTitle) { NSString *nsTitle = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; [self updateDisplayTitle:nsTitle]; } } - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; } - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + if ([_m_delegate respondsToSelector:@selector(webViewFailToLoad:)]) { [_m_delegate webViewFailToLoad:error]; } @@ -245,7 +245,6 @@ - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { [self isTopLevelNavigation:webView.request]) { } - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; } @end diff --git a/ios/SDWebViewController/SDWebViewDelegate.h b/ios/SDWebViewController/SDWebViewDelegate.h index e65ddd5..953bb1b 100755 --- a/ios/SDWebViewController/SDWebViewDelegate.h +++ b/ios/SDWebViewController/SDWebViewDelegate.h @@ -9,9 +9,12 @@ #import @protocol SDWebViewDelegate + @optional +- (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType; - (void)onWebViewWillClose:(UIWebView *)webView; - (void)onWebViewDidFinishLoad:(UIWebView *)webView; - (void)onWebViewDidStartLoad:(UIWebView *)webView; - (void)webViewFailToLoad:(NSError *)error; + @end From 0bda775c6ea988bd07eaa25ec43baf227746ad6b Mon Sep 17 00:00:00 2001 From: Dmitry Sytsevich Date: Thu, 30 May 2019 23:10:39 +0300 Subject: [PATCH 11/15] RNCloudPayments: bug fixes; --- ios/RNCloudPayments.m | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index 4de0d05..a2be544 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -1,13 +1,12 @@ #import "RNCloudPayments.h" #import "SDK/Card.m" -@interface RNCloudPayments () { - @property (nonatomic) RCTPromiseResolveBlock resolveWebView; -} +//@interface RNCloudPayments () { +// @property (nonatomic) RCTPromiseResolveBlock resolveWebView; +//} @implementation RNCloudPayments - RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(isValidNumber: (NSString *)cardNumber @@ -78,8 +77,6 @@ - (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *) // Detect url NSString *url = request.URL; NSString *sringUrl = request.URL.absoluteString; - - } - (void)onWebViewWillClose:(UIWebView *)webView { @@ -98,5 +95,4 @@ - (void)webViewFailToLoad:(NSError *)error { } - @end From a62f7ce8f7e8fa844cc1743fa4c6365ab2ea35ed Mon Sep 17 00:00:00 2001 From: Dmitry Sytsevich Date: Fri, 31 May 2019 18:45:57 +0300 Subject: [PATCH 12/15] MOBILE-27: change react-native-cloudpayments; --- ios/Extensions/NSString+URLEncoding.h | 15 ++++ ios/Extensions/NSString+URLEncoding.m | 34 ++++++++ ios/RNCloudPayments.h | 4 +- ios/RNCloudPayments.m | 82 ++++++++++++++----- ios/RNCloudPayments.xcodeproj/project.pbxproj | 14 ++++ ios/SDWebViewController/SDWebViewController.h | 1 + ios/SDWebViewController/SDWebViewController.m | 18 ++-- ios/SDWebViewController/SDWebViewDelegate.h | 2 +- 8 files changed, 139 insertions(+), 31 deletions(-) create mode 100644 ios/Extensions/NSString+URLEncoding.h create mode 100644 ios/Extensions/NSString+URLEncoding.m diff --git a/ios/Extensions/NSString+URLEncoding.h b/ios/Extensions/NSString+URLEncoding.h new file mode 100644 index 0000000..7925bfb --- /dev/null +++ b/ios/Extensions/NSString+URLEncoding.h @@ -0,0 +1,15 @@ +// +// NSString+URLEncoding.h +// +// Created by Dmitry Sytsevich on 5/30/19. +// Copyright © 2019 Dmitry Sytsevich. All rights reserved. +// + +#import + +@interface NSString (URLEncoding) + +- (NSString *)stringByURLEncoding; +- (NSString*)stringBetweenString:(NSString *)start andString:(NSString *)end; + +@end diff --git a/ios/Extensions/NSString+URLEncoding.m b/ios/Extensions/NSString+URLEncoding.m new file mode 100644 index 0000000..d323b58 --- /dev/null +++ b/ios/Extensions/NSString+URLEncoding.m @@ -0,0 +1,34 @@ +// +// NSString+URLEncoding.m +// +// Created by Dmitry Sytsevich on 5/30/19. +// Copyright © 2019 Dmitry Sytsevich. All rights reserved. +// + +#import "NSString+URLEncoding.h" + +@implementation NSString (URLEncoding) + +- (NSString *)stringByURLEncoding { + return (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, + (CFStringRef)self, + NULL, + (CFStringRef)@"!*'\"();:@+$,/?%#[]% ", CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); +} + +- (NSString*)stringBetweenString:(NSString *)start andString:(NSString *)end { + NSScanner* scanner = [NSScanner scannerWithString:(__bridge NSString * _Nonnull)((__bridge CFStringRef)self)]; + [scanner setCharactersToBeSkipped:nil]; + [scanner scanUpToString:start intoString:nil]; + if ([scanner scanString:start intoString:nil]) + { + NSString* result = nil; + if ([scanner scanUpToString:end intoString:&result]) + { + return result; + } + } + return nil; +} + +@end diff --git a/ios/RNCloudPayments.h b/ios/RNCloudPayments.h index 953a0f3..d5e052a 100644 --- a/ios/RNCloudPayments.h +++ b/ios/RNCloudPayments.h @@ -1,7 +1,5 @@ #import -#import "SDWebViewController.h" -#import "SDWebViewDelegate.h" -@interface RNCloudPayments : NSObject +@interface RNCloudPayments : NSObject @end diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index a2be544..49615ee 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -1,9 +1,22 @@ #import "RNCloudPayments.h" #import "SDK/Card.m" +#import "SDWebViewController/SDWebViewController.h" +#import "SDWebViewController/SDWebViewDelegate.h" +#import "NSString+URLEncoding.h" -//@interface RNCloudPayments () { -// @property (nonatomic) RCTPromiseResolveBlock resolveWebView; -//} +#define POST_BACK_URL @"https://demo.cloudpayments.ru/WebFormPost/GetWebViewData" + +typedef void (^RCTPromiseResolveBlock)(id result); +typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error); + +@interface RNCloudPayments () + +@property (nonatomic, retain) UINavigationController *navigationController; + +@property (nonatomic) RCTPromiseResolveBlock resolveWebView; +@property (nonatomic) RCTPromiseRejectBlock rejectWebView; + +@end @implementation RNCloudPayments @@ -39,7 +52,7 @@ @implementation RNCloudPayments { CardType cardType = [Card cardTypeFromCardNumber: cardNumber]; NSString *cardTypeString = [Card cardTypeToString: cardType]; - + resolve(cardTypeString); } @@ -51,9 +64,9 @@ @implementation RNCloudPayments reject: (RCTPromiseRejectBlock)reject) { Card *_card = [[Card alloc] init]; - + NSString *cryptogram = [_card makeCardCryptogramPacket: cardNumber andExpDate:cardExp andCVV:cardCvv andMerchantPublicID:publicId]; - + resolve(cryptogram); } @@ -63,11 +76,15 @@ @implementation RNCloudPayments resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject) { + self.resolveWebView = resolve; + self.rejectWebView = reject; + + // Show WebView SDWebViewController *webViewController = [[SDWebViewController alloc] initWithURL:url transactionId:transactionId token:token]; webViewController.m_delegate = self; - UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:webViewController]; - [navigationController.navigationBar setTranslucent:false]; - [[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:navigationController animated:YES completion:NULL]; + self.navigationController = [[UINavigationController alloc] initWithRootViewController:webViewController]; + [self.navigationController.navigationBar setTranslucent:false]; + [[self topViewController] presentViewController:self.navigationController animated:YES completion:nil]; } #pragma MARK: - SDWebViewDelegate @@ -75,24 +92,49 @@ @implementation RNCloudPayments - (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType { // Detect url - NSString *url = request.URL; - NSString *sringUrl = request.URL.absoluteString; -} - -- (void)onWebViewWillClose:(UIWebView *)webView { + NSString *urlString = request.URL.absoluteString; + if ([urlString isEqualToString:POST_BACK_URL]) { + NSString *result = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding]; + NSString *mdString = [result stringBetweenString:@"MD=" andString:@"&PaRes"]; + NSString *paResString = [result stringBetweenString:@"PaRes=" andString:@""]; + + NSDictionary *dictionary = @{@"MD": mdString, @"PaRes": paResString}; + + NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:nil]; + NSString *json = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + self.resolveWebView(json); + + [self.navigationController dismissViewControllerAnimated:YES completion:nil]; + } } -- (void)onWebViewDidFinishLoad:(UIWebView *)webView { - +- (void)webViewWillClose:(UIWebView *)webView { + self.rejectWebView(@"", @"", nil); } -- (void)onWebViewDidStartLoad:(UIWebView *)webView { - -} +#pragma MARK: - ViewController -- (void)webViewFailToLoad:(NSError *)error { +- (UIViewController *)topViewController { + UIViewController *baseVC = UIApplication.sharedApplication.keyWindow.rootViewController; + if ([baseVC isKindOfClass:[UINavigationController class]]) { + return ((UINavigationController *)baseVC).visibleViewController; + } + + if ([baseVC isKindOfClass:[UITabBarController class]]) { + UIViewController *selectedTVC = ((UITabBarController*)baseVC).selectedViewController; + if (selectedTVC) { + return selectedTVC; + } + } + + if (baseVC.presentedViewController) { + return baseVC.presentedViewController; + } + return baseVC; } @end diff --git a/ios/RNCloudPayments.xcodeproj/project.pbxproj b/ios/RNCloudPayments.xcodeproj/project.pbxproj index a0a489b..0fe10fc 100644 --- a/ios/RNCloudPayments.xcodeproj/project.pbxproj +++ b/ios/RNCloudPayments.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 954FC9F920AC08E60017B273 /* Card.m in Sources */ = {isa = PBXBuildFile; fileRef = 954FC9F620AC08E60017B273 /* Card.m */; }; 954FC9FA20AC08E60017B273 /* NSDataENBase64.m in Sources */ = {isa = PBXBuildFile; fileRef = 954FC9F720AC08E60017B273 /* NSDataENBase64.m */; }; AE8EA39622A015910033E067 /* SDWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AE8EA39522A015910033E067 /* SDWebViewController.m */; }; + AEB012DB22A1835200B733D5 /* NSString+URLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB012DA22A1835100B733D5 /* NSString+URLEncoding.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -36,6 +37,8 @@ AE8EA39322A015910033E067 /* SDWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebViewController.h; sourceTree = ""; }; AE8EA39422A015910033E067 /* SDWebViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebViewDelegate.h; sourceTree = ""; }; AE8EA39522A015910033E067 /* SDWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebViewController.m; sourceTree = ""; }; + AEB012D922A1835100B733D5 /* NSString+URLEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+URLEncoding.h"; sourceTree = ""; }; + AEB012DA22A1835100B733D5 /* NSString+URLEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+URLEncoding.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -52,6 +55,7 @@ 954FC8FE20AADA700017B273 = { isa = PBXGroup; children = ( + AEB012D822A1835100B733D5 /* Extensions */, 954FC90820AADA700017B273 /* Products */, 954FC90A20AADA700017B273 /* RNCloudPayments.h */, 954FC90B20AADA700017B273 /* RNCloudPayments.m */, @@ -89,6 +93,15 @@ path = SDWebViewController; sourceTree = ""; }; + AEB012D822A1835100B733D5 /* Extensions */ = { + isa = PBXGroup; + children = ( + AEB012D922A1835100B733D5 /* NSString+URLEncoding.h */, + AEB012DA22A1835100B733D5 /* NSString+URLEncoding.m */, + ); + path = Extensions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -144,6 +157,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + AEB012DB22A1835200B733D5 /* NSString+URLEncoding.m in Sources */, AE8EA39622A015910033E067 /* SDWebViewController.m in Sources */, 954FC90C20AADA700017B273 /* RNCloudPayments.m in Sources */, 954FC9F920AC08E60017B273 /* Card.m in Sources */, diff --git a/ios/SDWebViewController/SDWebViewController.h b/ios/SDWebViewController/SDWebViewController.h index e57f956..198d691 100755 --- a/ios/SDWebViewController/SDWebViewController.h +++ b/ios/SDWebViewController/SDWebViewController.h @@ -14,5 +14,6 @@ @property (weak, nonatomic) id m_delegate; - (id)initWithURL:(id)url transactionId:(NSString *)transactionId token:(NSString *)token; +- (NSString *)stringByURLEncoding; @end diff --git a/ios/SDWebViewController/SDWebViewController.m b/ios/SDWebViewController/SDWebViewController.m index 30c7afd..68a1581 100755 --- a/ios/SDWebViewController/SDWebViewController.m +++ b/ios/SDWebViewController/SDWebViewController.m @@ -14,6 +14,7 @@ #import "SDWebViewController.h" #import "SDWebViewDelegate.h" +#import "NSString+URLEncoding.h" @interface SDWebViewController () { @@ -167,6 +168,10 @@ - (void)initNavigationBarItem { } - (void)onCloseAction:(__unused id)sender { + if ([_m_delegate respondsToSelector:@selector(webViewWillClose:)]) { + [_m_delegate webViewWillClose:self.m_webView]; + } + [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; } @@ -181,13 +186,12 @@ - (BOOL)isTopLevelNavigation:(NSURLRequest *)req { } - (void)loadURL:(NSString *)url transactionId:(NSString *)transactionId token:(NSString *)token { - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; - NSString *post = [NSString stringWithFormat:@"MD=%@&PaReq=%@&TermUrl=%@", transactionId, token, POST_BACK_URL]; - NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postData]; - - [self.m_webView loadRequest:request]; + NSString *body = [NSString stringWithFormat: @"MD=%@&PaReq=%@&TermUrl=%@", token, transactionId, POST_BACK_URL]; + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString: url]]; + [request setHTTPMethod: @"POST"]; + body = [body stringByURLEncoding]; + [request setHTTPBody: [body dataUsingEncoding: NSUTF8StringEncoding]]; + [self.m_webView loadRequest: request]; } #pragma MARK: - UIWebViewDelegate diff --git a/ios/SDWebViewController/SDWebViewDelegate.h b/ios/SDWebViewController/SDWebViewDelegate.h index 953bb1b..f505c5d 100755 --- a/ios/SDWebViewController/SDWebViewDelegate.h +++ b/ios/SDWebViewController/SDWebViewDelegate.h @@ -12,7 +12,7 @@ @optional - (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType; -- (void)onWebViewWillClose:(UIWebView *)webView; +- (void)webViewWillClose:(UIWebView *)webView; - (void)onWebViewDidFinishLoad:(UIWebView *)webView; - (void)onWebViewDidStartLoad:(UIWebView *)webView; - (void)webViewFailToLoad:(NSError *)error; From 699e912ccbf587d3af0465f8822e6aeace29bec1 Mon Sep 17 00:00:00 2001 From: Dmitry Sytsevich Date: Fri, 31 May 2019 19:19:06 +0300 Subject: [PATCH 13/15] Fixed resolve data; --- ios/RNCloudPayments.m | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m index 49615ee..1414321 100644 --- a/ios/RNCloudPayments.m +++ b/ios/RNCloudPayments.m @@ -97,15 +97,11 @@ - (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *) if ([urlString isEqualToString:POST_BACK_URL]) { NSString *result = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding]; NSString *mdString = [result stringBetweenString:@"MD=" andString:@"&PaRes"]; - NSString *paResString = [result stringBetweenString:@"PaRes=" andString:@""]; + NSString *paResString = [[result stringBetweenString:@"PaRes=" andString:@""] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSDictionary *dictionary = @{@"MD": mdString, @"PaRes": paResString}; - NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:nil]; - NSString *json = [[NSString alloc] initWithData:data - encoding:NSUTF8StringEncoding]; - - self.resolveWebView(json); + self.resolveWebView(dictionary); [self.navigationController dismissViewControllerAnimated:YES completion:nil]; } From 082dbdb3d8243854b49bae01ebace8be0aae9f0c Mon Sep 17 00:00:00 2001 From: Mikhail Yarmoluk Date: Tue, 4 Jun 2019 15:57:32 +0300 Subject: [PATCH 14/15] Fix compatibility version --- ios/RNCloudPayments.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/RNCloudPayments.xcodeproj/project.pbxproj b/ios/RNCloudPayments.xcodeproj/project.pbxproj index 0fe10fc..31b1b04 100644 --- a/ios/RNCloudPayments.xcodeproj/project.pbxproj +++ b/ios/RNCloudPayments.xcodeproj/project.pbxproj @@ -136,7 +136,7 @@ }; }; buildConfigurationList = 954FC90220AADA700017B273 /* Build configuration list for PBXProject "RNCloudPayments" */; - compatibilityVersion = "Xcode 9.3"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( From a62a3e536b95ae1b9d2cee14f8c356280703f2d6 Mon Sep 17 00:00:00 2001 From: Ivan Vasilyev Date: Wed, 25 Dec 2019 17:13:13 +0300 Subject: [PATCH 15/15] add podspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support autolink for iOS project in ≥RN0.60 --- RNCloudPayments.podspec | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 RNCloudPayments.podspec diff --git a/RNCloudPayments.podspec b/RNCloudPayments.podspec new file mode 100644 index 0000000..531eb46 --- /dev/null +++ b/RNCloudPayments.podspec @@ -0,0 +1,14 @@ +Pod::Spec.new do |spec| + spec.name = "RNCloudPayments" + spec.version = "1.0.0" + spec.summary = "React Native library for using CloudPayments SDK" + spec.description = "React Native library for using CloudPayments SDK" + spec.homepage = "https://github.com/kakadu-dev/react-native-cloudpayments" + spec.license = { :type => "MIT" } + spec.author = { "Nikolay Polukhin" => "polu-hin@mail.ru" } + spec.platform = :ios + spec.ios.deployment_target = "9.0" + spec.source = { :git => "https://github.com/kakadu-dev/react-native-cloudpayments.git", :tag => "#{spec.version}" } + spec.source_files = "RNCloudPayments", "ios/*.{h,m}", "ios/Extensions/*.{h,m}", "ios/SDK/NSDataENBase64.{h,m}", "ios/SDWebViewController/*.{h,m}" + spec.dependency "React" + end