diff --git a/README.md b/README.md
index c1e0ba5..c8b4f50 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
+ });
+```
+
+### 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
});
@@ -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.
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
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..b1e1866
--- /dev/null
+++ b/android/src/main/java/com/rncloudpayments/CheckoutActivity.java
@@ -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);
+ }
+}
diff --git a/android/src/main/java/com/rncloudpayments/CloudPayments.java b/android/src/main/java/com/rncloudpayments/CloudPayments.java
index 269ca7b..855ebb0 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,10 @@ 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) {
@@ -31,10 +31,22 @@ public void isValidNumber(String cardNumber, String cardExp, String cardCvv, Pro
}
}
+ @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();
@@ -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);
@@ -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());
+ }
+ }
}
\ 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 c0cce8a..0000000
Binary files a/android/src/main/libs/CloudPayments.aar and /dev/null differ
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/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 3c88d5d..d5e052a 100644
--- a/ios/RNCloudPayments.h
+++ b/ios/RNCloudPayments.h
@@ -1,4 +1,5 @@
#import
@interface RNCloudPayments : NSObject
+
@end
diff --git a/ios/RNCloudPayments.m b/ios/RNCloudPayments.m
index 20e27be..1414321 100644
--- a/ios/RNCloudPayments.m
+++ b/ios/RNCloudPayments.m
@@ -1,13 +1,28 @@
#import "RNCloudPayments.h"
#import "SDK/Card.m"
+#import "SDWebViewController/SDWebViewController.h"
+#import "SDWebViewController/SDWebViewDelegate.h"
+#import "NSString+URLEncoding.h"
+
+#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
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(isValidNumber: (NSString *)cardNumber
- cardExp: (NSString *)cardExp
- cardCvv: (NSString *)cardCvv
resolve: (RCTPromiseResolveBlock)resolve
reject: (RCTPromiseRejectBlock)reject)
{
@@ -18,6 +33,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
@@ -43,4 +69,68 @@ @implementation RNCloudPayments
resolve(cryptogram);
}
+
+RCT_EXPORT_METHOD(show3DS: (NSString *)url
+ transactionId: (NSString *)transactionId
+ token: (NSString *)token
+ 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;
+ self.navigationController = [[UINavigationController alloc] initWithRootViewController:webViewController];
+ [self.navigationController.navigationBar setTranslucent:false];
+ [[self topViewController] presentViewController:self.navigationController animated:YES completion:nil];
+}
+
+#pragma MARK: - SDWebViewDelegate
+
+- (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType {
+
+ // Detect url
+ 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:@""] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+
+ NSDictionary *dictionary = @{@"MD": mdString, @"PaRes": paResString};
+
+ self.resolveWebView(dictionary);
+
+ [self.navigationController dismissViewControllerAnimated:YES completion:nil];
+ }
+}
+
+- (void)webViewWillClose:(UIWebView *)webView {
+ self.rejectWebView(@"", @"", nil);
+}
+
+#pragma MARK: - ViewController
+
+- (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 6993c14..31b1b04 100644
--- a/ios/RNCloudPayments.xcodeproj/project.pbxproj
+++ b/ios/RNCloudPayments.xcodeproj/project.pbxproj
@@ -10,6 +10,8 @@
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 */; };
+ AEB012DB22A1835200B733D5 /* NSString+URLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB012DA22A1835100B733D5 /* NSString+URLEncoding.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -32,6 +34,11 @@
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 = ""; };
+ 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 */
@@ -48,10 +55,12 @@
954FC8FE20AADA700017B273 = {
isa = PBXGroup;
children = (
- 954FC9F420AC08D70017B273 /* SDK */,
- 954FC90B20AADA700017B273 /* RNCloudPayments.m */,
- 954FC90A20AADA700017B273 /* RNCloudPayments.h */,
+ AEB012D822A1835100B733D5 /* Extensions */,
954FC90820AADA700017B273 /* Products */,
+ 954FC90A20AADA700017B273 /* RNCloudPayments.h */,
+ 954FC90B20AADA700017B273 /* RNCloudPayments.m */,
+ 954FC9F420AC08D70017B273 /* SDK */,
+ AE8EA39222A015910033E067 /* SDWebViewController */,
);
sourceTree = "";
};
@@ -74,6 +83,25 @@
path = SDK;
sourceTree = "";
};
+ AE8EA39222A015910033E067 /* SDWebViewController */ = {
+ isa = PBXGroup;
+ children = (
+ AE8EA39422A015910033E067 /* SDWebViewDelegate.h */,
+ AE8EA39322A015910033E067 /* SDWebViewController.h */,
+ AE8EA39522A015910033E067 /* SDWebViewController.m */,
+ );
+ 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 */
@@ -108,7 +136,7 @@
};
};
buildConfigurationList = 954FC90220AADA700017B273 /* Build configuration list for PBXProject "RNCloudPayments" */;
- compatibilityVersion = "Xcode 9.3";
+ compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -129,6 +157,8 @@
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 */,
954FC9FA20AC08E60017B273 /* NSDataENBase64.m in Sources */,
@@ -189,7 +219,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;
@@ -241,7 +271,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;
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
diff --git a/ios/SDWebViewController/SDWebViewController.h b/ios/SDWebViewController/SDWebViewController.h
new file mode 100755
index 0000000..198d691
--- /dev/null
+++ b/ios/SDWebViewController/SDWebViewController.h
@@ -0,0 +1,19 @@
+//
+// 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 transactionId:(NSString *)transactionId token:(NSString *)token;
+- (NSString *)stringByURLEncoding;
+
+@end
diff --git a/ios/SDWebViewController/SDWebViewController.m b/ios/SDWebViewController/SDWebViewController.m
new file mode 100755
index 0000000..68a1581
--- /dev/null
+++ b/ios/SDWebViewController/SDWebViewController.m
@@ -0,0 +1,254 @@
+//
+// 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]
+
+#define POST_BACK_URL @"https://demo.cloudpayments.ru/WebFormPost/GetWebViewData"
+
+#import "SDWebViewController.h"
+#import "SDWebViewDelegate.h"
+#import "NSString+URLEncoding.h"
+
+@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_transactionId;
+@property (strong, nonatomic) NSString *m_token;
+@property (strong, nonatomic) NSMutableDictionary *m_extraInfo;
+
+- (void)initWebView;
+- (void)initAddressBarView;
+- (void)removeAddressBar;
+- (void)initNavigationBarItem;
+
+@end
+
+@implementation SDWebViewController
+
+#pragma mark -
+
+- (id)initWithURL:(id)url transactionId:(NSString *)transactionId token:(NSString *)token {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.m_initUrl = url;
+ 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 loadURL:self.m_initUrl transactionId:self.m_transactionId token:self.m_token];
+}
+
+- (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 {
+ if ([_m_delegate respondsToSelector:@selector(webViewWillClose:)]) {
+ [_m_delegate webViewWillClose:self.m_webView];
+ }
+
+ [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)loadURL:(NSString *)url transactionId:(NSString *)transactionId token:(NSString *)token {
+ 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
+
+- (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];
+
+ return YES;
+}
+
+- (void)webViewDidStartLoad:(UIWebView *)webView {
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
+
+ if ([_m_delegate respondsToSelector:@selector(onWebViewDidStartLoad:)]) {
+ [_m_delegate onWebViewDidStartLoad:webView];
+ }
+
+ if ([self isTopLevelNavigation:webView.request]) {
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
+ }
+}
+
+- (void)webViewDidFinishLoad:(UIWebView *)webView {
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+
+ 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];
+
+ if (m_bAutoSetTitle) {
+ NSString *nsTitle = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
+ [self updateDisplayTitle:nsTitle];
+ }
+ }
+}
+
+- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+
+ if ([_m_delegate respondsToSelector:@selector(webViewFailToLoad:)]) {
+ [_m_delegate webViewFailToLoad:error];
+ }
+
+ if ([error code] != NSURLErrorCancelled &&
+ [self isTopLevelNavigation:webView.request]) {
+
+ }
+}
+
+@end
diff --git a/ios/SDWebViewController/SDWebViewDelegate.h b/ios/SDWebViewController/SDWebViewDelegate.h
new file mode 100755
index 0000000..f505c5d
--- /dev/null
+++ b/ios/SDWebViewController/SDWebViewDelegate.h
@@ -0,0 +1,20 @@
+//
+// SDWebViewDelegate.h
+// SDWebViewController
+//
+// Created by Dmitry Sytsevich on 5/30/19.
+// Copyright © 2019 Dmitry Sytsevich. All rights reserved.
+//
+
+#import
+
+@protocol SDWebViewDelegate
+
+@optional
+- (void)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType: (UIWebViewNavigationType)navigationType;
+- (void)webViewWillClose:(UIWebView *)webView;
+- (void)onWebViewDidFinishLoad:(UIWebView *)webView;
+- (void)onWebViewDidStartLoad:(UIWebView *)webView;
+- (void)webViewFailToLoad:(NSError *)error;
+
+@end