Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix iOS crush on show3DS call #3

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
48 changes: 38 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
```

### 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.isValidCard(demoCard.number, demoCard.extDate, demoCard.cvvCode)
RNCloudPayment.isValidExpired('11/21')
.then(cardStatus => {
console.log(cardStatus); // true
});
Expand Down Expand Up @@ -106,5 +115,24 @@ 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.

__Examples__
```js
import RNCloudPayment from 'react-native-cloudpayments';

RNCloudPayment.show3DS('https://demo.cloudpayments.ru', '1237618734', '....1d3d22r..')
.then(result => {
console.log(result);
});
```

# License
Licensed under the MIT License.
8 changes: 4 additions & 4 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand All @@ -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'
}
17 changes: 10 additions & 7 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<manifest
package="com.rncloudpayments"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.rncloudpayments">

<application
android:allowBackup="false"
tools:replace="android:allowBackup"/>
</manifest>
android:allowBackup="false"
tools:replace="android:allowBackup">
<activity android:name=".CheckoutActivity" />
</application>

</manifest>
66 changes: 66 additions & 0 deletions android/src/main/java/com/rncloudpayments/CheckoutActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.rncloudpayments;

import android.app.Activity;
import android.app.FragmentManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
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);
}
}
49 changes: 41 additions & 8 deletions android/src/main/java/com/rncloudpayments/CloudPayments.java
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -19,22 +20,33 @@ 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();
String validFormatNumber = cardNumber.replace(" ", "");
boolean numberStatus = CPCard.isValidNumber(validFormatNumber);

promise.resolve(numberStatus);
} catch (Exception e) {
promise.reject(e.getMessage());
}
}

@ReactMethod
public void isValidExpired(String cardExpired, Promise promise) {
try {
String validFormatExp = cardExpired.replace("/", "");
boolean expiredStatus = CPCard.isValidExpDate(validFormatExp);

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();

Expand All @@ -47,7 +59,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);

Expand All @@ -57,4 +72,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());
}
}
}
Binary file removed android/src/main/libs/CloudPayments.aar
Binary file not shown.
2 changes: 2 additions & 0 deletions android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<resources>
</resources>
20 changes: 18 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions ios/Extensions/NSString+URLEncoding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// NSString+URLEncoding.h
//
// Created by Dmitry Sytsevich on 5/30/19.
// Copyright © 2019 Dmitry Sytsevich. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSString (URLEncoding)

- (NSString *)stringByURLEncoding;
- (NSString*)stringBetweenString:(NSString *)start andString:(NSString *)end;

@end
34 changes: 34 additions & 0 deletions ios/Extensions/NSString+URLEncoding.m
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions ios/RNCloudPayments.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import <React/RCTBridgeModule.h>

@interface RNCloudPayments : NSObject <RCTBridgeModule>

@end
Loading