├── file-tree.png
├── enable_embedded.png
├── 4_enablekeychain_2x.png
├── android
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── arttitude360
│ │ └── reactnative
│ │ └── rnpaystack
│ │ ├── RNPaystackPackage.java
│ │ └── RNPaystackModule.java
└── build.gradle
├── react-native-paystack.podspec
├── package.json
├── ios
├── RNPaystack.h
├── RNPaystack.xcodeproj
│ └── project.pbxproj
└── RNPaystack.m
├── .gitignore
├── index.js
├── v2-Docs.md
├── Old Docs.md
├── README-3-1.md
└── README.md
/file-tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tolu360/react-native-paystack/HEAD/file-tree.png
--------------------------------------------------------------------------------
/enable_embedded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tolu360/react-native-paystack/HEAD/enable_embedded.png
--------------------------------------------------------------------------------
/4_enablekeychain_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tolu360/react-native-paystack/HEAD/4_enablekeychain_2x.png
--------------------------------------------------------------------------------
/android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RNPaystack
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/react-native-paystack.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, './package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = 'react-native-paystack'
7 | s.version = package['version']
8 | s.summary = package['description']
9 | s.description = package['description']
10 | s.license = package['license']
11 | s.author = package['author']
12 | s.homepage = 'https://github.com/tolu360/react-native-paystack'
13 | s.source = { :git => 'https://github.com/tolu360/react-native-paystack.git', :tag => "v#{s.version}" }
14 |
15 | s.platform = :ios, '9.0'
16 |
17 | s.preserve_paths = 'README.md', 'package.json', 'index.js'
18 | s.source_files = 'ios/*.{h,m}'
19 |
20 | s.compiler_flags = '-fno-modules'
21 |
22 | s.dependency 'React'
23 | s.dependency 'Paystack'
24 | end
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-paystack",
3 | "description": "React Native Wrappers for Paystack Android & iOS Mobile SDKs",
4 | "main": "index.js",
5 | "author": "Tolu Olowu (Arttitude 360) ",
6 | "version": "3.4.0",
7 | "scripts": {},
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/tolu360/react-native-paystack"
11 | },
12 | "license": "MIT",
13 | "bugs": {
14 | "url": "https://github.com/tolu360/react-native-paystack/issues"
15 | },
16 | "homepage": "https://github.com/tolu360/react-native-paystack#readme",
17 | "keywords": [
18 | "react",
19 | "react-native",
20 | "react-native module",
21 | "ios",
22 | "android",
23 | "paystack",
24 | "credit-card",
25 | "payments"
26 | ],
27 | "peerDependencies": {
28 | "react": ">=15.4.0 || >=16.x",
29 | "react-native": ">=0.40.0"
30 | },
31 | "devDependencies": {}
32 | }
--------------------------------------------------------------------------------
/ios/RNPaystack.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 |
6 | @interface RNPaystackModule : NSObject
7 |
8 | @property (nonatomic) NSString *errorMsg;
9 | @property (nonatomic) NSString *errorCode;
10 |
11 | - (BOOL)isCardNumberValid:(NSString *)cardNumber validateCardBrand:(BOOL)validateCardBrand;
12 | - (BOOL)isExpMonthValid:(NSString *)expMonth;
13 | - (BOOL)isExpYearValid:(NSString *)expYear forMonth:(NSString *)expMonth;
14 | - (BOOL)isCvcValid:(NSString *)cvc withNumber:(NSString *)cardNumber;
15 | - (BOOL)isCardValid:(PSTCKCardParams *)card;
16 | - (BOOL)cardParamsAreValid:(NSString *)cardNumber withMonth:(NSString *)expMonth withYear:(NSString *)expYear andWithCvc:(NSString *)cvc;
17 | - (NSMutableDictionary*)setTokenMsg:(NSString *)token withCardLastDigits:(NSString *)last4;
18 | - (NSMutableDictionary*)setReferenceMsg:(NSString *)reference;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 | npm-debug.log
29 |
30 | # OSX
31 | #
32 | .DS_Store
33 |
34 | examples/
35 |
36 | # Xcode
37 | #
38 | build/
39 | *.pbxuser
40 | !default.pbxuser
41 | *.mode1v3
42 | !default.mode1v3
43 | *.mode2v3
44 | !default.mode2v3
45 | *.perspectivev3
46 | !default.perspectivev3
47 | xcuserdata
48 | *.xccheckout
49 | *.moved-aside
50 | DerivedData
51 | *.hmap
52 | *.ipa
53 | *.xcuserstate
54 | project.xcworkspace
55 |
56 | # Android/IJ
57 | #
58 | react-native-dialogs.iml
59 | .idea
60 | .gradle
61 | local.properties
62 |
--------------------------------------------------------------------------------
/android/src/main/java/com/arttitude360/reactnative/rnpaystack/RNPaystackPackage.java:
--------------------------------------------------------------------------------
1 | package com.arttitude360.reactnative.rnpaystack;
2 |
3 |
4 | import com.facebook.react.ReactPackage;
5 | import com.facebook.react.bridge.JavaScriptModule;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.ArrayList;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class RNPaystackPackage implements ReactPackage {
15 |
16 | // Deprecated RN 0.47
17 | public List> createJSModules() {
18 | return Collections.emptyList();
19 | }
20 |
21 | @Override
22 | public List createViewManagers(ReactApplicationContext reactContext) {
23 | return Collections.emptyList();
24 | }
25 |
26 | @Override
27 | public List createNativeModules(ReactApplicationContext reactContext) {
28 | List modules = new ArrayList<>();
29 | modules.add(new RNPaystackModule(reactContext));
30 | return modules;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react'
3 |
4 | import { NativeModules, Platform } from 'react-native'
5 |
6 | const { RNPaystackModule } = NativeModules;
7 | const checkInit = (instance) => {
8 | if (!instance.paystackInitialized) {
9 | throw new Error(`You should call init first, higher up your code like in your index file.\nRead more https://github.com/tolu360/react-native-paystack#3-usage`)
10 | }
11 | }
12 |
13 | class RNPaystack {
14 | paystackInitialized = false;
15 |
16 | init(options: { [x: string]: any }) {
17 | if (typeof options != 'object') {
18 | return Promise.reject(new Error("Method argument can only be a Javascript object"));
19 | }
20 | this.paystackInitialized = true;
21 |
22 | return RNPaystackModule.init(options);
23 | }
24 |
25 | chargeCard(chargeParams: { [x: string]: any }) {
26 | if (typeof chargeParams != 'object') {
27 | return Promise.reject(new Error("Method argument can only be a Javascript object"));
28 | }
29 | checkInit(this);
30 |
31 | return RNPaystackModule.chargeCard(chargeParams);
32 | }
33 |
34 | chargeCardWithAccessCode(chargeParams: { [x: string]: any }) {
35 | if (typeof chargeParams != 'object') {
36 | return Promise.reject(new Error("Method argument can only be a Javascript object"));
37 | }
38 | checkInit(this);
39 |
40 | return RNPaystackModule.chargeCardWithAccessCode(chargeParams);
41 | }
42 | }
43 |
44 | export default new RNPaystack()
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 |
2 | apply plugin: 'com.android.library'
3 |
4 | def DEFAULT_COMPILE_SDK_VERSION = 28
5 | def DEFAULT_TARGET_SDK_VERSION = 28
6 | def DEFAULT_SUPPORT_LIB_VERSION = "28.0.0"
7 | def DEFAULT_MIN_SDK_VERSION = 16
8 |
9 | def safeExtGet(prop, fallback) {
10 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
11 | }
12 |
13 | android {
14 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
15 |
16 | defaultConfig {
17 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
18 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
19 | }
20 |
21 | packagingOptions {
22 | exclude 'META-INF/LICENSE'
23 | exclude 'META-INF/DEPENDENCIES.txt'
24 | exclude 'META-INF/LICENSE.txt'
25 | exclude 'META-INF/NOTICE.txt'
26 | exclude 'META-INF/NOTICE'
27 | exclude 'META-INF/DEPENDENCIES'
28 | exclude 'META-INF/notice.txt'
29 | exclude 'META-INF/license.txt'
30 | exclude 'META-INF/dependencies.txt'
31 | exclude 'META-INF/LGPL2.1'
32 | }
33 |
34 | lintOptions {
35 | disable 'InvalidPackage'
36 | }
37 | }
38 |
39 | dependencies {
40 | implementation fileTree(dir: "libs", include: ["*.jar"])
41 | implementation "com.facebook.react:react-native:+"
42 | implementation 'com.squareup.retrofit2:retrofit:2.5.0'
43 | implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
44 | implementation 'co.paystack.android.design.widget:pinpad:1.0.1'
45 | implementation 'co.paystack.android:paystack:3.0.12'
46 | }
--------------------------------------------------------------------------------
/v2-Docs.md:
--------------------------------------------------------------------------------
1 | ## Use the API below for v2 of this package only.
2 | - Download a fresh copy of the `Paystack iOS framework` from [Dropbox](https://www.dropbox.com/s/ykt5h0xjjkfwmk6/Paystack.framework.zip?dl=0) to continue to work with v2.0.0.
3 |
4 | ### Getting a Token (iOS & Android)
5 | It's a cinch to obtain a single-use card token using the react-native-paystack module. Pls note, the SDK assumes you are responsible for building the card form/UI.
6 |
7 | ```javascript
8 | RNPaystack.getToken(cardNumber, expiryMonth, expiryYear, cvc);
9 | ```
10 | To be more elaborate, `RNPaystack.getToken()` returns a Javascript `Promise` like:
11 |
12 | ```js
13 | import RNPaystack from 'react-native-paystack';
14 |
15 | getToken() {
16 |
17 | RNPaystack.getToken('4123450131001381', '01', '17', '883')
18 | .then(response => {
19 | console.log(response); // do stuff with the token
20 | })
21 | .catch(error => {
22 | console.log(error); // error is a javascript Error object
23 | console.log(error.message);
24 | console.log(error.code);
25 | })
26 |
27 | }
28 | ```
29 |
30 | #### Request Signature
31 |
32 | | Argument | Type | Description |
33 | | ------------- |:-------------:| :-----|
34 | | cardNumber | string | the card number as a String without any seperator e.g 5555555555554444 |
35 | | expiryMonth | string | the card expiry month as a double-digit ranging from 1-12 e.g 10 (October) |
36 | | expiryYear | string | the card expiry year as a double-digit e.g 15 |
37 | | cvc | string | the card 3/4 digit security code as a String e.g 123 |
38 |
39 | #### Response Object
40 |
41 | An object of the form is returned from a successful token request
42 |
43 | ```javascript
44 | {
45 | token: "PSTK_4aw6i0yizwvyzjx",
46 | last4: "1381"
47 | }
48 | ```
49 |
50 | ### Charging a Card (Android Only)
51 | Using the react-native-paystack module, you can complete the transaction with the Paystack Android SDK. Note that as with getting a card token, the SDK assumes you are responsible for building the card form/UI.
52 |
53 | ```javascript
54 | RNPaystack.chargeCard(cardNumber, expiryMonth, expiryYear, cvc, email, amountInKobo);
55 | ```
56 | To be more elaborate, `RNPaystack.chargeCard()` returns a Javascript `Promise` like:
57 |
58 | ```js
59 | import RNPaystack from 'react-native-paystack';
60 |
61 | chargeCard() {
62 |
63 | RNPaystack.chargeCard('4123450131001381', '01', '17', '883', 'dev-master@rnpaystack.dev', '10000')
64 | .then(response => {
65 | console.log(response); // card charged successfully, get reference here
66 | })
67 | .catch(error => {
68 | console.log(error); // error is a javascript Error object
69 | console.log(error.message);
70 | console.log(error.code);
71 | })
72 |
73 | }
74 | ```
75 |
76 | #### Request Signature
77 |
78 | | Argument | Type | Description |
79 | | ------------- |:-------------:| :-----|
80 | | cardNumber | string | the card number as a String without any seperator e.g 5555555555554444 |
81 | | expiryMonth | string | the card expiry month as a double-digit ranging from 1-12 e.g 10 (October) |
82 | | expiryYear | string | the card expiry year as a double-digit e.g 15 |
83 | | cvc | string | the card 3/4 digit security code as e.g 123 |
84 | | email | string | email of the user to be charged |
85 | | amountInKobo | integer | the transaction amount in kobo |
86 |
87 | #### Response Object
88 |
89 | An object of the form is returned from a successful charge
90 |
91 | ```javascript
92 | {
93 | reference: "trx_1k2o600w"
94 | }
95 | ```
--------------------------------------------------------------------------------
/Old Docs.md:
--------------------------------------------------------------------------------
1 | # React Native Wrapper for Paystack Mobile SDKs
2 |
3 | for Android & iOS by [Arttitude 360](http://www.arttitude360.com)
4 |
5 | ## Index
6 |
7 | 1. [Description](#1-description)
8 | 2. [Installation](#2-installation)
9 | 3. [Usage](#3-usage)
10 | 4. [Credits](#4-credits)
11 | 5. [Changelog](#5-changelog)
12 | 6. [License](#6-license)
13 |
14 | ## 1. Description
15 |
16 | This React Native module provides a wrapper to add Paystack Payments to your React Native application using the [Paystack Android Mobile SDK](https://github.com/PaystackHQ/paystack-android) and the [Paystack iOS Mobile SDK](https://github.com/PaystackHQ/paystack-ios) libraries.
17 |
18 | ## 2. Installation
19 |
20 | You can pull in react-native-paystack via npm:
21 |
22 | ```shell
23 | npm install react-native-paystack --save
24 | ```
25 |
26 | ### Configuration
27 |
28 | #### IOS
29 |
30 | 1. In XCode's "Project navigator", right click on project name folder ➜ `Add Files to <...>`
31 | - Ensure `Copy items if needed` and `Create groups` are checked
32 | 2. Go to `node_modules` ➜ `react-native-paystack` ➜ add `paystack-ios` folder
33 | 3. To be sure you are all set, manually inspect your project target settings - Ensure:
34 | + path to `paystack-ios` folder is set in `Build Settings > Search Paths > Framework Search Paths`
35 | + `PaystackIOS.m` is listed in `Build Phases > Compile Sources`
36 | + `Paystack.framework` is listed in `Build Phases > Link Binary with Libraries`.
37 | 4. Edit your `AppDelegate.m` and import the Paystack framework:
38 |
39 | ```Objective-C
40 | #import
41 | ...
42 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
43 | {
44 | ...
45 | [Paystack setDefaultPublishableKey:@"INSERT-PUBLIC-KEY-HERE"];
46 | ...
47 |
48 | }
49 | ```
50 | *Compile and have some card tokens!*
51 |
52 |
53 | #### Android
54 |
55 | #### Step 1 - Update Gradle Settings
56 |
57 | ```gradle
58 | // file: android/settings.gradle
59 | ...
60 |
61 | include ':reactnativepaystack'
62 | project(':reactnativepaystack').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-paystack/paystack-android')
63 | ```
64 |
65 | #### Step 2 - Update app Gradle Build
66 |
67 | ```gradle
68 | // file: android/app/build.gradle
69 | ...
70 |
71 | dependencies {
72 | ...
73 | compile project(':reactnativepaystack')
74 | }
75 | ```
76 |
77 | #### Step 3 - Register React Package
78 |
79 | // file: android/app/src/main/java/.../MainActivity
80 |
81 | ##### react-native >= v0.18.0
82 |
83 | ```java
84 | import com.arttitude360.reactnativepaystack.ReactNativePaystackPackage; // <-- import
85 | ...
86 | /**
87 | * A list of packages used by the app. If the app uses additional views
88 | * or modules besides the default ones, add more packages here.
89 | */
90 | @Override
91 | protected List getPackages() {
92 | return Arrays.asList(
93 | new MainReactPackage(),
94 | new ReactNativePaystackPackage()); // <-- Register package here
95 | }
96 | ```
97 |
98 |
99 | Add the following tag in your `android/app/src/main/AndroidManifest.xml` file:
100 |
101 | ```xml
102 |
103 | ```
104 |
105 | ## 3. Usage
106 |
107 | ### Getting a Token
108 | It's a cinch to obtain a single-use token with the PaystackSdk using the react-native-paystack module.
109 |
110 | ```js
111 | PaystackAndroid.getToken(cardNumber, expiryMonth, expiryYear, cvc, errorCallback, successCallback);
112 | PaystackIOS.getToken(cardNumber, expiryMonth, expiryYear, cvc, responseCallback);
113 | ```
114 | To be more elaborate:
115 |
116 | #### On Android
117 |
118 | ```js
119 | import PaystackAndroid from 'react-native-paystack';
120 |
121 | componentDidMount() {
122 | PaystackAndroid.getToken(
123 | '4123450131001384',
124 | '05',
125 | '16',
126 | '883',
127 | (resp) => {
128 | // didn't get a token - something went wrong
129 | this.setState({error: resp.error});
130 | console.log(resp);
131 | },
132 | (resp) => {
133 | // got a token - do your thang!
134 | this.setState({token: resp.token});
135 | console.log(resp);
136 | }
137 | );
138 | }
139 | ```
140 |
141 | #### On iOS
142 |
143 | ```js
144 | import PaystackIOS from 'react-native-paystack';
145 |
146 | componentDidMount() {
147 | PaystackIOS.getToken(
148 | '4123450131001384',
149 | '05',
150 | '16',
151 | '883',
152 | (error, resp) => {
153 | if (error) {
154 | // didn't get a token - something went wrong
155 | this.setState({errorMsg: error.error});
156 | }
157 | if (resp) {
158 | // got a token - do your thang!
159 | this.setState({tokenStr: resp.token});
160 | }
161 | }
162 | );
163 | }
164 | ```
165 |
166 | Explaining the arguments to `PaystackAndroid.getToken`:
167 |
168 | + {Function} responseCallback (only on iOS) - callback to be invoked with the result of calling `getToken` - takes in 2 arguments - (error, response). If any error is set, you get an error object with 2 keys: "error" is a string containing a description of the error, "code" is an arbitrary error code.
169 | If a token is returned, you get a response object with 2 keys: "token" is a string containing the returned token, while "last4" is a string containing the last 4 digits of the card the token belongs to.
170 | + {Function} successCallback (only on android) - callback to be invoked on successfully acquiring a token.
171 | * A single object argument will be passed which has 2 keys: "token" is a string containing the returned token, while "last4" is a string containing the last 4 digits of the card the token belongs to.
172 | + {Function} errorCallback (only on android) - callback to be invoked on failure to acquire a valid token.
173 | * A single object argument will be passed which has 2 keys: "error" is a string containing a description of the error, "code" is an arbitrary error code.
174 | + cardNumber: the card number as a String without any seperator e.g 5555555555554444
175 | + expiryMonth: the expiry month as an integer ranging from 1-12 e.g 10 (October)
176 | + expiryYear: the expiry year as an integer e.g 15 (2 digits - very !important for iOS)
177 | + cvc: the card security code as a String e.g 123
178 | + To make it easy for you, you can pass all string or all integer arguments - the module will handle the type castings for you.
179 |
180 | ### Charging the tokens.
181 | Send the token to your server and create a charge by calling the Paystack REST API. An authorization_code will be returned once the single-use token has been charged successfully. You can learn more about the Paystack API [here](https://developers.paystack.co/docs/getting-started).
182 |
183 | **Endpoint:** https://api.paystack.co/transaction/charge_token
184 |
185 | **Parameters:**
186 |
187 | - email - customer's email address (required)
188 | - reference - unique reference (required)
189 | - amount - Amount in Kobo (required)
190 |
191 | **Example**
192 |
193 | ```bash
194 | curl https://api.paystack.co/transaction/charge_token \
195 | -H "Authorization: Bearer SECRET_KEY" \
196 | -H "Content-Type: application/json" \
197 | -d '{"token": "PSTK_r4ec2m75mrgsd8n9", "email": "customer@email.com", "amount": 10000, "reference": "amutaJHSYGWakinlade256"}' \
198 | -X POST
199 |
200 | ```
201 |
202 |
203 | ## 4. CREDITS
204 |
205 | Perhaps needless to say, this module leverages the [Paystack Android SDK](https://github.com/PaystackHQ/paystack-android) and the [Paystack IOS SDK](https://github.com/PaystackHQ/paystack-ios) for all the heavy liftings.
206 |
207 | ## 5. CHANGELOG
208 |
209 | + 1.0.12: initial version supporting Android.
210 | + 1.1.1: android library upgrade and initial iOS support.
211 |
212 | ## 6. License
213 |
214 | This should be [The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html). I would have to get back to you on that!
215 |
216 |
--------------------------------------------------------------------------------
/ios/RNPaystack.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B3E7B58A1CC2AC0600A0062D /* RNPaystack.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNPaystack.m */; };
11 | /* End PBXBuildFile section */
12 |
13 | /* Begin PBXCopyFilesBuildPhase section */
14 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
15 | isa = PBXCopyFilesBuildPhase;
16 | buildActionMask = 2147483647;
17 | dstPath = "include/$(PRODUCT_NAME)";
18 | dstSubfolderSpec = 16;
19 | files = (
20 | );
21 | runOnlyForDeploymentPostprocessing = 0;
22 | };
23 | /* End PBXCopyFilesBuildPhase section */
24 |
25 | /* Begin PBXFileReference section */
26 | 134814201AA4EA6300B7C361 /* libRNPaystack.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNPaystack.a; sourceTree = BUILT_PRODUCTS_DIR; };
27 | B3E7B5881CC2AC0600A0062D /* RNPaystack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNPaystack.h; sourceTree = ""; };
28 | B3E7B5891CC2AC0600A0062D /* RNPaystack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNPaystack.m; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 134814211AA4EA7D00B7C361 /* Products */ = {
43 | isa = PBXGroup;
44 | children = (
45 | 134814201AA4EA6300B7C361 /* libRNPaystack.a */,
46 | );
47 | name = Products;
48 | sourceTree = "";
49 | };
50 | 58B511D21A9E6C8500147676 = {
51 | isa = PBXGroup;
52 | children = (
53 | B3E7B5881CC2AC0600A0062D /* RNPaystack.h */,
54 | B3E7B5891CC2AC0600A0062D /* RNPaystack.m */,
55 | 134814211AA4EA7D00B7C361 /* Products */,
56 | );
57 | sourceTree = "";
58 | };
59 | /* End PBXGroup section */
60 |
61 | /* Begin PBXNativeTarget section */
62 | 58B511DA1A9E6C8500147676 /* RNPaystack */ = {
63 | isa = PBXNativeTarget;
64 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNPaystack" */;
65 | buildPhases = (
66 | 58B511D71A9E6C8500147676 /* Sources */,
67 | 58B511D81A9E6C8500147676 /* Frameworks */,
68 | 58B511D91A9E6C8500147676 /* CopyFiles */,
69 | );
70 | buildRules = (
71 | );
72 | dependencies = (
73 | );
74 | name = RNPaystack;
75 | productName = RCTDataManager;
76 | productReference = 134814201AA4EA6300B7C361 /* libRNPaystack.a */;
77 | productType = "com.apple.product-type.library.static";
78 | };
79 | /* End PBXNativeTarget section */
80 |
81 | /* Begin PBXProject section */
82 | 58B511D31A9E6C8500147676 /* Project object */ = {
83 | isa = PBXProject;
84 | attributes = {
85 | LastUpgradeCheck = 0610;
86 | ORGANIZATIONNAME = Facebook;
87 | TargetAttributes = {
88 | 58B511DA1A9E6C8500147676 = {
89 | CreatedOnToolsVersion = 6.1.1;
90 | };
91 | };
92 | };
93 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNPaystack" */;
94 | compatibilityVersion = "Xcode 3.2";
95 | developmentRegion = English;
96 | hasScannedForEncodings = 0;
97 | knownRegions = (
98 | en,
99 | );
100 | mainGroup = 58B511D21A9E6C8500147676;
101 | productRefGroup = 58B511D21A9E6C8500147676;
102 | projectDirPath = "";
103 | projectRoot = "";
104 | targets = (
105 | 58B511DA1A9E6C8500147676 /* RNPaystack */,
106 | );
107 | };
108 | /* End PBXProject section */
109 |
110 | /* Begin PBXSourcesBuildPhase section */
111 | 58B511D71A9E6C8500147676 /* Sources */ = {
112 | isa = PBXSourcesBuildPhase;
113 | buildActionMask = 2147483647;
114 | files = (
115 | B3E7B58A1CC2AC0600A0062D /* RNPaystack.m in Sources */,
116 | );
117 | runOnlyForDeploymentPostprocessing = 0;
118 | };
119 | /* End PBXSourcesBuildPhase section */
120 |
121 | /* Begin XCBuildConfiguration section */
122 | 58B511ED1A9E6C8500147676 /* Debug */ = {
123 | isa = XCBuildConfiguration;
124 | buildSettings = {
125 | ALWAYS_SEARCH_USER_PATHS = NO;
126 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
127 | CLANG_CXX_LIBRARY = "libc++";
128 | CLANG_ENABLE_MODULES = YES;
129 | CLANG_ENABLE_OBJC_ARC = YES;
130 | CLANG_WARN_BOOL_CONVERSION = YES;
131 | CLANG_WARN_CONSTANT_CONVERSION = YES;
132 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
133 | CLANG_WARN_EMPTY_BODY = YES;
134 | CLANG_WARN_ENUM_CONVERSION = YES;
135 | CLANG_WARN_INT_CONVERSION = YES;
136 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
137 | CLANG_WARN_UNREACHABLE_CODE = YES;
138 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
139 | COPY_PHASE_STRIP = NO;
140 | ENABLE_STRICT_OBJC_MSGSEND = YES;
141 | GCC_C_LANGUAGE_STANDARD = gnu99;
142 | GCC_DYNAMIC_NO_PIC = NO;
143 | GCC_OPTIMIZATION_LEVEL = 0;
144 | GCC_PREPROCESSOR_DEFINITIONS = (
145 | "DEBUG=1",
146 | "$(inherited)",
147 | );
148 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
149 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
150 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
151 | GCC_WARN_UNDECLARED_SELECTOR = YES;
152 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
153 | GCC_WARN_UNUSED_FUNCTION = YES;
154 | GCC_WARN_UNUSED_VARIABLE = YES;
155 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
156 | MTL_ENABLE_DEBUG_INFO = YES;
157 | ONLY_ACTIVE_ARCH = YES;
158 | SDKROOT = iphoneos;
159 | };
160 | name = Debug;
161 | };
162 | 58B511EE1A9E6C8500147676 /* Release */ = {
163 | isa = XCBuildConfiguration;
164 | buildSettings = {
165 | ALWAYS_SEARCH_USER_PATHS = NO;
166 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
167 | CLANG_CXX_LIBRARY = "libc++";
168 | CLANG_ENABLE_MODULES = YES;
169 | CLANG_ENABLE_OBJC_ARC = YES;
170 | CLANG_WARN_BOOL_CONVERSION = YES;
171 | CLANG_WARN_CONSTANT_CONVERSION = YES;
172 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
173 | CLANG_WARN_EMPTY_BODY = YES;
174 | CLANG_WARN_ENUM_CONVERSION = YES;
175 | CLANG_WARN_INT_CONVERSION = YES;
176 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
177 | CLANG_WARN_UNREACHABLE_CODE = YES;
178 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
179 | COPY_PHASE_STRIP = YES;
180 | ENABLE_NS_ASSERTIONS = NO;
181 | ENABLE_STRICT_OBJC_MSGSEND = YES;
182 | GCC_C_LANGUAGE_STANDARD = gnu99;
183 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
184 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
185 | GCC_WARN_UNDECLARED_SELECTOR = YES;
186 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
187 | GCC_WARN_UNUSED_FUNCTION = YES;
188 | GCC_WARN_UNUSED_VARIABLE = YES;
189 | IPHONEOS_DEPLOYMENT_TARGET = 7.0;
190 | MTL_ENABLE_DEBUG_INFO = NO;
191 | SDKROOT = iphoneos;
192 | VALIDATE_PRODUCT = YES;
193 | };
194 | name = Release;
195 | };
196 | 58B511F01A9E6C8500147676 /* Debug */ = {
197 | isa = XCBuildConfiguration;
198 | buildSettings = {
199 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../../../ios";
200 | HEADER_SEARCH_PATHS = (
201 | "$(inherited)",
202 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
203 | "$(SRCROOT)/../../../React/**",
204 | "$(SRCROOT)/../../react-native/React/**",
205 | "$(SRCROOT)/../react-native/React/**",
206 | "$(SRCROOT)/../../../ios/Paystack/**",
207 | "$(SRCROOT)/../../../ios/Pods/Headers/Public/**",
208 | );
209 | LIBRARY_SEARCH_PATHS = "$(inherited)";
210 | OTHER_LDFLAGS = "-ObjC";
211 | PRODUCT_NAME = RNPaystack;
212 | SKIP_INSTALL = YES;
213 | };
214 | name = Debug;
215 | };
216 | 58B511F11A9E6C8500147676 /* Release */ = {
217 | isa = XCBuildConfiguration;
218 | buildSettings = {
219 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../../../ios";
220 | HEADER_SEARCH_PATHS = (
221 | "$(inherited)",
222 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
223 | "$(SRCROOT)/../../../React/**",
224 | "$(SRCROOT)/../../react-native/React/**",
225 | "$(SRCROOT)/../react-native/React/**",
226 | "$(SRCROOT)/../../../ios/Paystack/**",
227 | "$(SRCROOT)/../../../ios/Pods/Headers/Public/**",
228 | );
229 | LIBRARY_SEARCH_PATHS = "$(inherited)";
230 | OTHER_LDFLAGS = "-ObjC";
231 | PRODUCT_NAME = RNPaystack;
232 | SKIP_INSTALL = YES;
233 | };
234 | name = Release;
235 | };
236 | /* End XCBuildConfiguration section */
237 |
238 | /* Begin XCConfigurationList section */
239 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNPaystack" */ = {
240 | isa = XCConfigurationList;
241 | buildConfigurations = (
242 | 58B511ED1A9E6C8500147676 /* Debug */,
243 | 58B511EE1A9E6C8500147676 /* Release */,
244 | );
245 | defaultConfigurationIsVisible = 0;
246 | defaultConfigurationName = Release;
247 | };
248 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNPaystack" */ = {
249 | isa = XCConfigurationList;
250 | buildConfigurations = (
251 | 58B511F01A9E6C8500147676 /* Debug */,
252 | 58B511F11A9E6C8500147676 /* Release */,
253 | );
254 | defaultConfigurationIsVisible = 0;
255 | defaultConfigurationName = Release;
256 | };
257 | /* End XCConfigurationList section */
258 | };
259 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
260 | }
261 |
--------------------------------------------------------------------------------
/android/src/main/java/com/arttitude360/reactnative/rnpaystack/RNPaystackModule.java:
--------------------------------------------------------------------------------
1 | package com.arttitude360.reactnative.rnpaystack;
2 |
3 | import android.app.Activity;
4 | import android.util.Log;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.content.Intent;
8 | import android.util.Patterns;
9 |
10 | import co.paystack.android.Paystack;
11 | import co.paystack.android.PaystackSdk;
12 | import co.paystack.android.model.Card;
13 | import co.paystack.android.model.Charge;
14 | import co.paystack.android.Transaction;
15 |
16 | import org.json.JSONArray;
17 | import org.json.JSONException;
18 | import org.json.JSONObject;
19 |
20 | import com.facebook.react.bridge.Arguments;
21 | import com.facebook.react.bridge.Promise;
22 | import com.facebook.react.bridge.ReactApplicationContext;
23 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
24 | import com.facebook.react.bridge.ReactMethod;
25 | import com.facebook.react.bridge.WritableMap;
26 | import com.facebook.react.bridge.WritableNativeMap;
27 | import com.facebook.react.bridge.ReadableArray;
28 | import com.facebook.react.bridge.ReadableMap;
29 | import com.facebook.react.bridge.ReadableMapKeySetIterator;
30 |
31 | public class RNPaystackModule extends ReactContextBaseJavaModule {
32 |
33 | protected Card card;
34 | private Charge charge;
35 | private Transaction transaction;
36 |
37 | private ReactApplicationContext reactContext;
38 | private Promise pendingPromise;
39 | private ReadableMap chargeOptions;
40 | private String mPublicKey;
41 |
42 | public static final String TAG = "RNPaystack";
43 |
44 | public static String REACT_CLASS = "RNPaystackModule";
45 |
46 | private static RNPaystackModule sInstance = null;
47 |
48 | public static RNPaystackModule getInstance() {
49 | return sInstance;
50 | }
51 |
52 | public RNPaystackModule(ReactApplicationContext reactContext) {
53 | super(reactContext);
54 |
55 | this.reactContext = reactContext;
56 | sInstance = this;
57 |
58 | // Initialize PaystackSdk
59 | PaystackSdk.initialize(this.reactContext);
60 | }
61 |
62 | @Override
63 | public String getName() {
64 | return REACT_CLASS;
65 | }
66 |
67 | @ReactMethod
68 | public void init(ReadableMap options) {
69 | String newPublicKey = options.getString("publicKey");
70 |
71 | if (newPublicKey != null) {
72 | mPublicKey = newPublicKey;
73 | PaystackSdk.setPublicKey(newPublicKey);
74 | }
75 | }
76 |
77 | @ReactMethod
78 | public void chargeCard(ReadableMap cardData, final Promise promise) {
79 |
80 | this.chargeOptions = null;
81 |
82 | this.pendingPromise = promise;
83 | this.chargeOptions = cardData;
84 |
85 | validateFullTransaction();
86 |
87 | if (card != null && card.isValid()) {
88 | try {
89 | createTransaction();
90 | } catch (Exception error) {
91 | rejectPromise("E_CHARGE_ERROR", error.getMessage());
92 | }
93 |
94 | }
95 | }
96 |
97 | @ReactMethod
98 | public void chargeCardWithAccessCode(ReadableMap cardData, final Promise promise) {
99 |
100 | this.chargeOptions = null;
101 |
102 | this.pendingPromise = promise;
103 | this.chargeOptions = cardData;
104 |
105 | validateAccessCodeTransaction();
106 |
107 | if (card != null && card.isValid()) {
108 | try {
109 | createTransaction();
110 | } catch (Exception error) {
111 | rejectPromise("E_CHARGE_ERROR", error.getMessage());
112 | }
113 |
114 | }
115 | }
116 |
117 | private void validateCard(String cardNumber, String expiryMonth, String expiryYear, String cvc) {
118 |
119 | if (isEmpty(cardNumber)) {
120 | rejectPromise("E_INVALID_NUMBER", "Empty card number");
121 | return;
122 | }
123 |
124 | // build card object with ONLY the number, update the other fields later
125 | card = new Card.Builder(cardNumber, 0, 0, "").build();
126 |
127 | if (!card.validNumber()) {
128 | rejectPromise("E_INVALID_NUMBER", "Invalid card number");
129 | return;
130 | }
131 |
132 | // validate cvc
133 | if (isEmpty(cvc)) {
134 | rejectPromise("E_INVALID_CVC", "Empty CVC");
135 | return;
136 | }
137 |
138 | // update the cvc field of the card
139 | card.setCvc(cvc);
140 |
141 | // check that it's valid
142 | if (!card.validCVC()) {
143 | rejectPromise("E_INVALID_CVC", "Invalid CVC");
144 | return;
145 | }
146 |
147 | int month = -1;
148 | try {
149 | month = Integer.parseInt(expiryMonth);
150 | } catch (Exception ignored) {
151 | }
152 |
153 | // validate expiry month
154 | if (month < 1) {
155 | rejectPromise("E_INVALID_MONTH", "Invalid expiration month");
156 | return;
157 | }
158 |
159 | // update the expiryMonth field of the card
160 | card.setExpiryMonth(month);
161 |
162 | int year = -1;
163 | try {
164 | year = Integer.parseInt(expiryYear);
165 | } catch (Exception ignored) {
166 | }
167 |
168 | // validate expiry year
169 | if (year < 1) {
170 | rejectPromise("E_INVALID_YEAR", "Invalid expiration year");
171 | return;
172 | }
173 |
174 | // update the expiryYear field of the card
175 | card.setExpiryYear(year);
176 |
177 | // validate expiry
178 | if (!card.validExpiryDate()) {
179 | rejectPromise("E_INVALID_DATE", "Invalid expiration date");
180 | return;
181 | }
182 | }
183 |
184 | private void validateAccessCodeTransaction() {
185 | String cardNumber = chargeOptions.getString("cardNumber");
186 | String expiryMonth = chargeOptions.getString("expiryMonth");
187 | String expiryYear = chargeOptions.getString("expiryYear");
188 | String cvc = chargeOptions.getString("cvc");
189 |
190 | validateCard(cardNumber, expiryMonth, expiryYear, cvc);
191 |
192 | charge = new Charge();
193 | charge.setCard(card);
194 |
195 | if (hasStringKey("accessCode")) {
196 | charge.setAccessCode(chargeOptions.getString("accessCode"));
197 | }
198 | }
199 |
200 | private void validateFullTransaction() {
201 |
202 | String cardNumber = chargeOptions.getString("cardNumber");
203 | String expiryMonth = chargeOptions.getString("expiryMonth");
204 | String expiryYear = chargeOptions.getString("expiryYear");
205 | String cvc = chargeOptions.getString("cvc");
206 | String email = chargeOptions.getString("email");
207 | int amountInKobo = chargeOptions.getInt("amountInKobo");
208 |
209 | validateCard(cardNumber, expiryMonth, expiryYear, cvc);
210 |
211 | charge = new Charge();
212 | charge.setCard(card);
213 |
214 | if (isEmpty(email)) {
215 | rejectPromise("E_INVALID_EMAIL", "Email cannot be empty");
216 | return;
217 | }
218 |
219 | if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
220 | rejectPromise("E_INVALID_EMAIL", "Invalid email");
221 | return;
222 | }
223 |
224 | charge.setEmail(email);
225 |
226 | if (amountInKobo < 1) {
227 | rejectPromise("E_INVALID_AMOUNT", "Invalid amount");
228 | return;
229 | }
230 |
231 | charge.setAmount(amountInKobo);
232 |
233 | if (hasStringKey("currency")) {
234 | charge.setCurrency(chargeOptions.getString("currency"));
235 | }
236 |
237 | if (hasStringKey("plan")) {
238 | charge.setPlan(chargeOptions.getString("plan"));
239 | }
240 |
241 | if (hasStringKey("subAccount")) {
242 | charge.setSubaccount(chargeOptions.getString("subAccount"));
243 |
244 | if (hasStringKey("bearer") && chargeOptions.getString("bearer") == "subaccount") {
245 | charge.setBearer(Charge.Bearer.subaccount);
246 | }
247 |
248 | if (hasStringKey("bearer") && chargeOptions.getString("bearer") == "account") {
249 | charge.setBearer(Charge.Bearer.account);
250 | }
251 |
252 | if (hasIntKey("transactionCharge")) {
253 | charge.setTransactionCharge(chargeOptions.getInt("transactionCharge"));
254 | }
255 | }
256 |
257 | if (hasStringKey("reference")) {
258 | charge.setReference(chargeOptions.getString("reference"));
259 | }
260 |
261 | }
262 |
263 | private void createTransaction() {
264 |
265 | transaction = null;
266 | Activity currentActivity = getCurrentActivity();
267 |
268 | PaystackSdk.chargeCard(currentActivity, charge, new Paystack.TransactionCallback() {
269 | @Override
270 | public void onSuccess(Transaction transaction) {
271 |
272 | // This is called only after transaction is successful
273 | RNPaystackModule.this.transaction = transaction;
274 |
275 | WritableMap map = Arguments.createMap();
276 | map.putString("reference", transaction.getReference());
277 |
278 | resolvePromise(map);
279 | }
280 |
281 | @Override
282 | public void beforeValidate(Transaction transaction) {
283 | // This is called only before requesting OTP
284 | // Save reference so you may send to server if
285 | // error occurs with OTP
286 | RNPaystackModule.this.transaction = transaction;
287 | }
288 |
289 | @Override
290 | public void onError(Throwable error, Transaction transaction) {
291 | RNPaystackModule.this.transaction = transaction;
292 |
293 | if (transaction.getReference() == null) {
294 | rejectPromise("E_TRANSACTION_ERROR", error.getMessage());
295 | } else {
296 | rejectPromise("E_TRANSACTION_ERROR",
297 | transaction.getReference() + " concluded with error: " + error.getMessage());
298 | }
299 | }
300 |
301 | });
302 | }
303 |
304 | private boolean isEmpty(String s) {
305 | return s == null || s.length() < 1;
306 | }
307 |
308 | private boolean hasStringKey(String key) {
309 | return chargeOptions.hasKey(key) && !chargeOptions.isNull(key) && !chargeOptions.getString(key).isEmpty();
310 | }
311 |
312 | private boolean hasIntKey(String key) {
313 | return chargeOptions.hasKey(key) && !chargeOptions.isNull(key) && chargeOptions.getInt(key) > 0;
314 | }
315 |
316 | private void rejectPromise(String code, String message) {
317 | if (this.pendingPromise != null) {
318 | this.pendingPromise.reject(code, message);
319 | this.pendingPromise = null;
320 | }
321 | }
322 |
323 | private void resolvePromise(Object data) {
324 | if (this.pendingPromise != null) {
325 | this.pendingPromise.resolve(data);
326 | this.pendingPromise = null;
327 | }
328 | }
329 |
330 | }
--------------------------------------------------------------------------------
/ios/RNPaystack.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import "RNPaystack.h"
3 | #import
4 | #import
5 |
6 | @implementation RNPaystackModule
7 | {
8 | RCTPromiseResolveBlock _resolve;
9 | RCTPromiseRejectBlock _reject;
10 | NSString *publicKey;
11 | BOOL requestIsCompleted;
12 | }
13 |
14 | - (instancetype)init {
15 | if ((self = [super init])) {
16 | requestIsCompleted = YES;
17 | }
18 | return self;
19 | }
20 |
21 | - (dispatch_queue_t)methodQueue {
22 | return dispatch_get_main_queue();
23 | }
24 |
25 | + (BOOL)requiresMainQueueSetup
26 | {
27 | return YES;
28 | }
29 |
30 | RCT_EXPORT_MODULE();
31 |
32 | - (BOOL)isCardNumberValid:(NSString *)cardNumber validateCardBrand:(BOOL)validateCardBrand
33 | {
34 | BOOL isValid = ([PSTCKCardValidator validationStateForNumber:cardNumber validatingCardBrand:validateCardBrand] == PSTCKCardValidationStateValid);
35 | return isValid;
36 | }
37 |
38 | - (BOOL)isExpMonthValid:(NSString *)expMonth
39 | {
40 | BOOL isValid = ([PSTCKCardValidator validationStateForExpirationMonth:expMonth] == PSTCKCardValidationStateValid);
41 | return isValid;
42 | }
43 |
44 | - (BOOL)isExpYearValid:(NSString *)expYear forMonth:(NSString *)expMonth
45 | {
46 | BOOL isValid = ([PSTCKCardValidator validationStateForExpirationYear:expYear inMonth:expMonth] == PSTCKCardValidationStateValid);
47 | return isValid;
48 | }
49 |
50 | - (BOOL)isCvcValid:(NSString *)cvc withNumber:(NSString *)cardNumber
51 | {
52 | BOOL isValid = ([PSTCKCardValidator validationStateForCVC:cvc cardBrand:[PSTCKCardValidator brandForNumber:cardNumber]] == PSTCKCardValidationStateValid);
53 | return isValid;
54 | }
55 |
56 | - (BOOL)isCardValid:(PSTCKCardParams *)card
57 | {
58 | BOOL isValid = ([PSTCKCardValidator validationStateForCard:card] == PSTCKCardValidationStateValid);
59 | return isValid;
60 | }
61 |
62 | - (NSMutableDictionary*)setTokenMsg:(NSString *)token withCardLastDigits:(NSString *)last4
63 | {
64 | NSMutableDictionary *returnInfo;
65 | returnInfo = [NSMutableDictionary dictionaryWithCapacity:2];
66 |
67 | [returnInfo setObject:token forKey:@"token"];
68 | [returnInfo setObject:last4 forKey:@"last4"];
69 |
70 | return returnInfo;
71 | }
72 |
73 | - (NSMutableDictionary*)setReferenceMsg:(NSString *)reference
74 | {
75 | NSMutableDictionary *returnInfo;
76 | returnInfo = [NSMutableDictionary dictionaryWithCapacity:1];
77 |
78 | [returnInfo setObject:reference forKey:@"reference"];
79 |
80 | return returnInfo;
81 | }
82 |
83 | - (BOOL)cardParamsAreValid:(NSString *)cardNumber withMonth:(NSString *)expMonth withYear:(NSString *)expYear andWithCvc:(NSString *)cvc
84 | {
85 | if (! [self isCardNumberValid:cardNumber validateCardBrand:YES]) {
86 | self.errorMsg = @"Invalid card number";
87 | self.errorCode = @"E_INVALID_NUMBER";
88 | return NO;
89 | }
90 |
91 | if (! [self isExpMonthValid:expMonth]) {
92 | self.errorMsg = @"Invalid expiration month";
93 | self.errorCode = @"E_INVALID_MONTH";
94 | return NO;
95 | }
96 |
97 | if (! [self isExpYearValid:expYear forMonth:expMonth]) {
98 | self.errorMsg = @"Invalid expiration year";
99 | self.errorCode = @"E_INVALID_YEAR";
100 | return NO;
101 | }
102 |
103 | if (! [self isCvcValid:cvc withNumber:cardNumber]) {
104 | self.errorMsg = @"Invalid CVC";
105 | self.errorCode = @"E_INVALID_CVC";
106 | return NO;
107 | }
108 |
109 | return YES;
110 |
111 | }
112 |
113 | RCT_EXPORT_METHOD(init:(NSDictionary *)options) {
114 | publicKey = options[@"publicKey"];
115 | [Paystack setDefaultPublicKey:publicKey];
116 | }
117 |
118 | RCT_EXPORT_METHOD(chargeCard:(NSDictionary *)params
119 | resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
120 | {
121 | _resolve = resolve;
122 | _reject = reject;
123 | UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
124 |
125 | if(!requestIsCompleted) {
126 | _reject(@"E_BUSY", @"Another request is still being processed, please wait", nil);
127 | return;
128 | }
129 |
130 | requestIsCompleted = NO;
131 |
132 | if (! [self cardParamsAreValid:params[@"cardNumber"] withMonth:params[@"expiryMonth"] withYear:params[@"expiryYear"] andWithCvc:params[@"cvc"]]) {
133 |
134 | // NSMutableDictionary *returnInfo = [self setErrorMsg:self.errorMsg withErrorCode:self.errorCode];
135 | if (_reject) {
136 | _reject(self.errorCode, self.errorMsg, nil);
137 | }
138 |
139 | } else {
140 |
141 | PSTCKCardParams *cardParams = [[PSTCKCardParams alloc] init];
142 | cardParams.number = params[@"cardNumber"];
143 | cardParams.expMonth = [params[@"expiryMonth"] integerValue];
144 | cardParams.expYear = [params[@"expiryYear"] integerValue];
145 | cardParams.cvc = params[@"cvc"];
146 |
147 | PSTCKTransactionParams *transactionParams = [[PSTCKTransactionParams alloc] init];
148 | transactionParams.amount = [params[@"amountInKobo"] integerValue];
149 | transactionParams.email = params[@"email"];
150 |
151 | if (params[@"currency"] != nil) {
152 | transactionParams.currency = params[@"currency"];
153 | }
154 |
155 | if (params[@"plan"] != nil) {
156 | transactionParams.plan = params[@"plan"];
157 | }
158 |
159 | if (params[@"subAccount"] != nil) {
160 | transactionParams.subaccount = params[@"subAccount"];
161 |
162 | if (params[@"bearer"] != nil) {
163 | transactionParams.bearer = params[@"bearer"];
164 | }
165 |
166 | if (params[@"transactionCharge"] != nil) {
167 | transactionParams.transaction_charge = [params[@"transactionCharge"] integerValue];
168 | }
169 | }
170 |
171 | if (params[@"reference"] != nil) {
172 | transactionParams.reference = params[@"reference"];
173 | }
174 |
175 | if ([self isCardValid:cardParams]) {
176 |
177 | [[PSTCKAPIClient sharedClient] chargeCard:cardParams
178 | forTransaction:transactionParams
179 | onViewController:rootViewController
180 | didEndWithError:^(NSError *error, NSString *reference){
181 | requestIsCompleted = YES;
182 | if (_reject) {
183 | _reject(@"E_CHARGE_ERROR", @"Error charging card", error);
184 | }
185 | }
186 | didRequestValidation: ^(NSString *reference){
187 | // an OTP was requested, transaction has not yet succeeded
188 | NSLog(@"- RNPaystack: an OTP was requested, transaction has not yet succeeded");
189 | }
190 | didTransactionSuccess: ^(NSString *reference){
191 | requestIsCompleted = YES;
192 | // transaction may have succeeded, please verify on server
193 | NSLog(@"- RNPaystack: transaction may have succeeded, please verify on server");
194 | NSMutableDictionary *returnInfo = [self setReferenceMsg:reference];
195 |
196 | if (_resolve) {
197 | _resolve(returnInfo);
198 | }
199 | }];
200 |
201 | } else {
202 | requestIsCompleted = YES;
203 |
204 | if (_reject) {
205 | _reject(@"E_INVALID_CARD", @"Card is invalid", nil);
206 | }
207 | }
208 |
209 | }
210 | }
211 |
212 | RCT_EXPORT_METHOD(chargeCardWithAccessCode:(NSDictionary *)params
213 | resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
214 | {
215 | _resolve = resolve;
216 | _reject = reject;
217 | UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
218 |
219 | if(!requestIsCompleted) {
220 | _reject(@"E_BUSY", @"Another request is still being processed, please wait", nil);
221 | return;
222 | }
223 |
224 | requestIsCompleted = NO;
225 |
226 | if (! [self cardParamsAreValid:params[@"cardNumber"] withMonth:params[@"expiryMonth"] withYear:params[@"expiryYear"] andWithCvc:params[@"cvc"]]) {
227 |
228 | // NSMutableDictionary *returnInfo = [self setErrorMsg:self.errorMsg withErrorCode:self.errorCode];
229 | if (_reject) {
230 | _reject(self.errorCode, self.errorMsg, nil);
231 | }
232 |
233 | } else {
234 |
235 | PSTCKCardParams *cardParams = [[PSTCKCardParams alloc] init];
236 | cardParams.number = params[@"cardNumber"];
237 | cardParams.expMonth = [params[@"expiryMonth"] integerValue];
238 | cardParams.expYear = [params[@"expiryYear"] integerValue];
239 | cardParams.cvc = params[@"cvc"];
240 |
241 | PSTCKTransactionParams *transactionParams = [[PSTCKTransactionParams alloc] init];
242 | transactionParams.access_code = params[@"accessCode"];
243 |
244 | if ([self isCardValid:cardParams]) {
245 |
246 | [[PSTCKAPIClient sharedClient] chargeCard:cardParams
247 | forTransaction:transactionParams
248 | onViewController:rootViewController
249 | didEndWithError:^(NSError *error, NSString *reference){
250 | requestIsCompleted = YES;
251 | if (_reject) {
252 | _reject(@"E_CHARGE_ERROR", @"Error charging card", error);
253 | }
254 | }
255 | didRequestValidation: ^(NSString *reference){
256 | // an OTP was requested, transaction has not yet succeeded
257 | NSLog(@"- RNPaystack: an OTP was requested, transaction has not yet succeeded");
258 | }
259 | didTransactionSuccess: ^(NSString *reference){
260 | requestIsCompleted = YES;
261 | // transaction may have succeeded, please verify on server
262 | NSLog(@"- RNPaystack: transaction may have succeeded, please verify on server");
263 | NSMutableDictionary *returnInfo = [self setReferenceMsg:reference];
264 |
265 | if (_resolve) {
266 | _resolve(returnInfo);
267 | }
268 | }];
269 |
270 | } else {
271 | requestIsCompleted = YES;
272 |
273 | if (_reject) {
274 | _reject(@"E_INVALID_CARD", @"Card is invalid", nil);
275 | }
276 | }
277 |
278 | }
279 |
280 | }
281 |
282 | @end
283 |
--------------------------------------------------------------------------------
/README-3-1.md:
--------------------------------------------------------------------------------
1 | # React Native Wrapper for Paystack Mobile SDKs
2 |
3 | for Android & iOS by [Arttitude 360](http://www.arttitude360.com)
4 |
5 | ## Index
6 |
7 | 1. [Description](#1-description)
8 | 2. [Installation](#2-installation)
9 | 3. [Usage](#3-usage)
10 | 4. [Credits](#4-credits)
11 | 5. [Changelog](#5-changelog)
12 | 6. [License](#6-license)
13 |
14 | ## 1. Description
15 |
16 | This React Native module provides a wrapper to add Paystack Payments to your React Native application using the [Paystack Android Mobile SDK](https://github.com/PaystackHQ/paystack-android) and the [Paystack iOS Mobile SDK](https://github.com/PaystackHQ/paystack-ios) libraries. If interested, there is a [Sample App](https://github.com/tolu360/vestarapp).
17 |
18 | ## 2. Installation
19 |
20 | You can pull in react-native-paystack via npm:
21 |
22 | ```shell
23 | npm install react-native-paystack --save
24 | ```
25 | OR
26 |
27 | ```shell
28 | yarn add react-native-paystack
29 | ```
30 |
31 | ### Versioning
32 | - For `RN <=0.39` use version 2+ e.g. react-native-paystack@2.2.0 (No longer updated)
33 | - For `RN >=0.40` use version 3+ e.g. react-native-paystack@3.1.4
34 |
35 | ### Configuration
36 |
37 | #### Automatic (iOS & Android)
38 |
39 | ```shell
40 | react-native link react-native-paystack
41 | ```
42 | - (iOS only): The next steps are necessary for iOS at this time as publishing to NPM seems to break symlinks contained in the Paystack iOS framework shipped with this package, thus causing XCode build errors.
43 | - Download a fresh copy of the `Paystack iOS framework` from their [releases page on Github](https://github.com/PaystackHQ/paystack-ios/releases/).
44 | - Extract `Paystack.framework` from the downloaded zip.
45 | - In XCode's "Project navigator", right click on project name folder ➜ `Add Files to `. Ensure `Copy items if needed` and `Create groups` are checked and select your copy of `Paystack.framework`.
46 | - Your files tree in XCode should look similar to the screenshot below:
47 |
48 |
49 |
50 | - If you are working with XCode 8+, to allow encryptions work properly with the Paystack SDK, you may need to enable `Keychain Sharing` for your app. In the Capabilities pane, if Keychain Sharing isn’t enabled, toggle ON the switch in the Keychain Sharing section.
51 |
52 |
53 |
54 | #### Manual Config (iOS)
55 |
56 | - The following steps are optional, should be taken if you have not run `react-native link react-native-paystack` already.
57 | - In XCode's "Project navigator", right click on project name folder ➜ `Add Files to <...>`. Ensure `Copy items if needed` and `Create groups` are checked
58 | - Go to `node_modules` ➜ `react-native-paystack/ios` ➜ add `RNPaystack.xcodeproj`.
59 |
60 | #### Manual Config (Android)
61 |
62 | - The following steps are optional, should be taken if you have not run `react-native link react-native-paystack` already.
63 | - Add the following in your `android/settings.gradle` file:
64 |
65 | ```java
66 | include ':react-native-paystack'
67 | project(':react-native-paystack').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-paystack/android')
68 | ```
69 | - Add the following in your `android/app/build.grade` file:
70 |
71 | ```java
72 | dependencies {
73 | ...
74 | compile project(':react-native-paystack')
75 | }
76 | ```
77 | - Add the following in your `...MainApplication.java` file:
78 |
79 | ```java
80 | import com.arttitude360.reactnative.rnpaystack.RNPaystackPackage;
81 |
82 | @Override
83 | protected List getPackages() {
84 | return Arrays.asList(
85 | new MainReactPackage(),
86 | ...
87 | new RNPaystackPackage() //<-- Add line
88 | );
89 | }
90 | ```
91 |
92 | #### More Config (Android v3.1.4+)
93 | - Update Gradle plugin to v3.0.0 for your app, follow the following steps if you are not sure how:
94 | * Edit your `~ android/build.gradle` to look similar to [build.gradle](https://github.com/tolu360/vestarapp/blob/master/android/build.gradle)
95 | * Edit your `~ android/gradle/wrapper/gradle-wrapper.properties` to look similar to [gradle-wrapper.properties](https://github.com/tolu360/vestarapp/blob/master/android/gradle/wrapper/gradle-wrapper.properties)
96 |
97 | - Update your Android build tools and environment to v27+ after the Gradle plugin update
98 | * Edit your `~ android/app/build.gradle` to look similar to [build.gradle](https://github.com/tolu360/vestarapp/blob/master/android/app/build.gradle)
99 |
100 |
101 | ## 3. Usage
102 |
103 | ### Import Library
104 | - For ios, edit your `AppDelegate.m` file and import the Paystack framework:
105 |
106 | ```Objective-C
107 | #import
108 | ...
109 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
110 | {
111 | ...
112 |
113 | [Paystack setDefaultPublicKey:@"INSERT-PUBLIC-KEY-HERE"];
114 | ...
115 |
116 | }
117 | ```
118 |
119 | - For Android, add the following tag in your `android/app/src/main/AndroidManifest.xml` file within the `` tags:
120 |
121 | ```xml
122 |
123 | ```
124 |
125 | ### Charging a Card with Access Code (iOS & Android)
126 | It's a cinch to charge a card token using the react-native-paystack module. This is the recommended or the most-preferred workflow favored by the folks at Paystack. Initiate a new transaction on your server side using the appropriate [Paystack endpoint](https://developers.paystack.co/reference#initialize-a-transaction) - obtain an `access_code` and complete the charge on your mobile application. Pls note, the SDK assumes you are responsible for building the card form/UI.
127 |
128 | ```javascript
129 | RNPaystack.chargeCardWithAccessCode(cardParams);
130 | ```
131 | To be more elaborate, `cardParams` is a Javascript `Object` representing the card to be charged and `RNPaystack.chargeCardWithAccessCode()` returns a Javascript `Promise` like:
132 |
133 | ```js
134 | import RNPaystack from 'react-native-paystack';
135 |
136 | chargeCard() {
137 |
138 | RNPaystack.chargeCardWithAccessCode({
139 | cardNumber: '4123450131001381',
140 | expiryMonth: '10',
141 | expiryYear: '17',
142 | cvc: '883',
143 | accessCode: '2p3j42th639duy4'
144 | })
145 | .then(response => {
146 | console.log(response); // do stuff with the token
147 | })
148 | .catch(error => {
149 | console.log(error); // error is a javascript Error object
150 | console.log(error.message);
151 | console.log(error.code);
152 | })
153 |
154 | }
155 | ```
156 |
157 | #### Request Signature
158 |
159 | | Argument | Type | Description |
160 | | ------------- |:-------------:| :-----|
161 | | cardNumber | string | the card number as a String without any seperator e.g 5555555555554444 |
162 | | expiryMonth | string | the card expiry month as a double-digit ranging from 1-12 e.g 10 (October) |
163 | | expiryYear | string | the card expiry year as a double-digit e.g 15 |
164 | | cvc | string | the card 3/4 digit security code as a String e.g 123 |
165 | | accessCode | string | the access_code obtained for the charge |
166 |
167 | #### Response Object
168 |
169 | An object of the form is returned from a successful token request
170 |
171 | ```javascript
172 | {
173 | reference: "trx_1k2o600w"
174 | }
175 | ```
176 |
177 | ### Charging a Card (iOS & Android)
178 | Using the react-native-paystack module, you can start and complete a transaction with the mobile Paystack Android and iOS SDKs. With this option, you pass both your charge and card properties to the SDK - with this worklow, you initiate and complete a transaction on your mobile app. Note that as with charging with an access_code, the SDK assumes you are responsible for building the card form/UI.
179 |
180 | ```javascript
181 | RNPaystack.chargeCard(chargeParams);
182 | ```
183 | To be more elaborate, `chargeParams` is a Javascript `Object` representing the parameters of the charge to be initiated and `RNPaystack.chargeCard()` returns a Javascript `Promise` like:
184 |
185 | ```js
186 | import RNPaystack from 'react-native-paystack';
187 |
188 | chargeCard() {
189 |
190 | RNPaystack.chargeCard({
191 | cardNumber: '4123450131001381',
192 | expiryMonth: '10',
193 | expiryYear: '17',
194 | cvc: '883',
195 | email: 'chargeIOS@master.dev',
196 | amountInKobo: 150000,
197 | subAccount: 'ACCT_pz61jjjsslnx1d9',
198 | })
199 | .then(response => {
200 | console.log(response); // card charged successfully, get reference here
201 | })
202 | .catch(error => {
203 | console.log(error); // error is a javascript Error object
204 | console.log(error.message);
205 | console.log(error.code);
206 | })
207 |
208 | }
209 | ```
210 |
211 | #### Request Signature (chargeParams)
212 |
213 | | Argument | Type | Description |
214 | | ------------- |:-------------:| :-----|
215 | | cardNumber | string | the card number as a String without any seperator e.g 5555555555554444 |
216 | | expiryMonth | string | the card expiry month as a double-digit ranging from 1-12 e.g 10 (October) |
217 | | expiryYear | string | the card expiry year as a double-digit e.g 15 |
218 | | cvc | string | the card 3/4 digit security code as e.g 123 |
219 | | email | string | email of the user to be charged |
220 | | amountInKobo | integer | the transaction amount in kobo |
221 | | currency (optional) | string | sets the currency for the transaction e.g. USD |
222 | | plan (optional) | string | sets the plan ID if the transaction is to create a subscription e.g. PLN_n0p196bg73y4jcx |
223 | | subAccount (optional) | string | sets the subaccount ID for split-payment transactions e.g. ACCT_pz61jjjsslnx1d9 |
224 | | transactionCharge (optional) | integer | the amount to be charged on a split-payment, use only when `subAccount` is set |
225 | | bearer (optional) | string | sets which party bears paystack fees on a split-payment e.g. 'subaccount', use only when `subAccount` is set |
226 | | reference (optional) | string | sets the transaction reference which must be unique per transaction |
227 |
228 | #### Response Object
229 |
230 | An object of the form is returned from a successful charge
231 |
232 | ```javascript
233 | {
234 | reference: "trx_1k2o600w"
235 | }
236 | ```
237 |
238 | ### Verifying a Charge
239 | Verify a charge by calling Paystack's [REST API](https://api.paystack.co/transaction/verify) with the `reference` obtained above. An `authorization_code` will be returned once the card has been charged successfully. Learn more about that [here](https://developers.paystack.co/docs/verify-transaction).
240 |
241 | **Parameter:**
242 |
243 | - reference - the transaction reference (required)
244 |
245 | **Example**
246 |
247 | ```bash
248 | $ curl https://api.paystack.co/transaction/verify/trx_1k2o600w \
249 | -H "Authorization: Bearer SECRET_KEY" \
250 | -H "Content-Type: application/json" \
251 | -X GET
252 | ```
253 |
254 | ## 4. CREDITS
255 |
256 | Perhaps needless to say, this module leverages the [Paystack Android SDK](https://github.com/PaystackHQ/paystack-android) and the [Paystack IOS SDK](https://github.com/PaystackHQ/paystack-ios) for all the heavy liftings.
257 |
258 | ## 5. CHANGELOG
259 |
260 | + 1.0.12: Initial version supporting Android.
261 | + 1.1.1: Android library upgrade and initial iOS support.
262 | + 2.0.0: A couple of breaking changes have been introduced, see [Old Docs](./Old Docs.md) for previous documentations.
263 | + 2.0.0: Upgraded to v2.0 of the Paystack Android SDK.
264 | + 2.0.0: Unified APIs across both platforms (iOS & Android).
265 | + 2.0.0: Methods now return Javascript Promises on both platforms.
266 | + 2.1.1: Upgraded to v2.1+ of both the Paystack iOS and Android SDKs.
267 | + 2.1.1: Added support for `chargeCard` on both platforms.
268 | + 2.1.1: Added support for `subscriptions` and `split-payments`.
269 | + 3.1.0: Retired support for `getToken` on both platforms.
270 | + 3.1.0: Added support for `chargeCardWithAccessCode` on both platforms.
271 | + 3.1.0: Upgraded to v3.*+ of both the Paystack iOS and Android SDKs.
272 | + 3.1.1: Fix for breaking change in RN v0.47+
273 | * 3.1.4: Miscellaneous and dependencies update on Android
274 |
275 | ## 6. License
276 |
277 | This should be [The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html). I would have to get back to you on that!
278 |
279 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Native Wrapper for Paystack Mobile SDKs
2 |
3 | for Android & iOS by [Arttitude 360](http://www.arttitude360.com)
4 |
5 | ## Index
6 |
7 | 1. [Description](#1-description)
8 | 2. [Installation](#2-installation)
9 | 3. [Usage](#3-usage)
10 | 4. [Credits](#4-credits)
11 | 5. [Changelog](#5-changelog)
12 | 6. [License](#6-license)
13 |
14 | ## 1. Description
15 |
16 | This React Native module provides a wrapper to add Paystack Payments to your React Native application using the [Paystack Android Mobile SDK](https://github.com/PaystackHQ/paystack-android) and the [Paystack iOS Mobile SDK](https://github.com/PaystackHQ/paystack-ios) libraries.
17 |
18 | ### PS: If you are using this library in production, please give the repo a star - I am not being vain and have no interest in the vanity metric, just trying to figure out if it is still worth the time or effort spent supporting the library. Cheers!
19 |
20 | ## 2. Installation
21 |
22 | You can pull in react-native-paystack via npm:
23 |
24 | ```shell
25 | npm install react-native-paystack --save
26 | ```
27 | OR
28 |
29 | ```shell
30 | yarn add react-native-paystack
31 | ```
32 |
33 | ### Versioning
34 | - For `RN >=0.40` only;
35 | - Breaking Change Alert for v3.2.0+. Looking for the docs for the 3.1.* version of this library? [Check here!](./README-3-1.md)
36 |
37 | ### Configuration
38 |
39 | #### Post-Install Steps (iOS)
40 |
41 | ##### 1) Auto Linking & Cocoapods Integration (React Native 0.59 and lower)
42 | - If you do not have CocoaPods already installed on your machine, run `gem install cocoapods` to set it up the first time. (Hint: Go grab a cup of coffee!)
43 | - If you are not using Cocoapods in your project already, run `cd ios && pod init` at the root directory of your project. This would create a `Podfile` in your `ios` directory.
44 | - Run `react-native link react-native-paystack` at the root directory of your project and ensure you edit your Podfile to look like the sample below (remove all the targets you are not building for, such as Tests and tvOS):
45 |
46 | ```ruby
47 | # platform :ios, '9.0'
48 |
49 | target '_YOUR_PROJECT_TARGET_' do
50 |
51 | # Pods for _YOUR_PROJECT_TARGET_
52 | pod 'React', :path => '../node_modules/react-native', :subspecs => [
53 | 'Core',
54 | 'CxxBridge',
55 | 'DevSupport',
56 | 'RCTText',
57 | 'RCTImage',
58 | 'RCTNetwork',
59 | 'RCTWebSocket',
60 | 'RCTSettings',
61 | 'RCTAnimation',
62 | 'RCTLinkingIOS',
63 | # Add any other subspecs you want to use in your project
64 | # Remove any subspecs you don't want to use in your project
65 | ]
66 |
67 | pod "yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
68 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
69 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
70 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
71 | # This should already be auto-added for you, if not add the line below
72 | pod 'react-native-paystack', :path => '../node_modules/react-native-paystack'
73 |
74 | end
75 | ```
76 |
77 | - Replace all references to _YOUR_PROJECT_TARGET_ with your project target (it's the same as project name by default).
78 | - By now, you should be all set to install the packages from your Podfile. Run `pod install` from your `ios` directory.
79 | - Close Xcode, and then open (double-click) your project's .xcworkspace file to launch Xcode. From this time onwards, you must use the `.xcworkspace` file to open the project. Or just use the `react-native run-ios` command as usual to run your app in the simulator.
80 |
81 | ##### 2) Auto Linking & Cocoapods Integration (React Native 0.60 and higher)
82 | Since React Native 0.60 and higher, [autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md) makes the installation process simpler.
83 |
84 | ```sh
85 | cd ios
86 | pod install
87 | ```
88 | - Close Xcode, and then open (double-click) your project's .xcworkspace file to launch Xcode. From this time onwards, you must use the `.xcworkspace` file to open the project. Or just use the `react-native run-ios` command as usual to run your app in the simulator.
89 |
90 | #### Manual Config (iOS)
91 |
92 | - The following steps are optional, should be taken if you have not run `react-native link react-native-paystack` already.
93 | - In XCode's "Project navigator", right click on project name folder ➜ `Add Files to <...>`. Ensure `Copy items if needed` and `Create groups` are checked
94 | - Go to `node_modules` ➜ `react-native-paystack/ios` ➜ add `RNPaystack.xcodeproj`.
95 | - Click on your main project file (the one that represents the .xcodeproj for your project) select `Build Phases` and drag the static library, `libRNPaystack.a` from the `Products` folder inside `RNPaystack.xcodeproj` to `Link Binary With Libraries`. See the [react-native docs](https://facebook.github.io/react-native/docs/linking-libraries-ios.html) for more details.
96 |
97 | #### Autolinking on Android (React Native 0.59 and lower)
98 | - Run `react-native link react-native-paystack` at the root directory of your project.
99 |
100 | #### Autolinking on Android (React Native 0.60 and higher)
101 | Since React Native 0.60 and higher, [autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md) makes the installation process simpler. Nothing more to do here (Gradle has you all set to go) - just head over to usage!
102 |
103 | #### Manual Config (Android)
104 |
105 | - The following steps are optional, should be taken if you have not run `react-native link react-native-paystack` already.
106 | - Add the following in your `android/settings.gradle` file:
107 |
108 | ```java
109 | include ':react-native-paystack'
110 | project(':react-native-paystack').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-paystack/android')
111 | ```
112 | - Add the following in your `android/app/build.grade` file:
113 |
114 | ```java
115 | dependencies {
116 | ...
117 | compile project(':react-native-paystack')
118 | }
119 | ```
120 | - Add the following in your `...MainApplication.java` file:
121 |
122 | ```java
123 | import com.arttitude360.reactnative.rnpaystack.RNPaystackPackage;
124 |
125 | @Override
126 | protected List getPackages() {
127 | return Arrays.asList(
128 | new MainReactPackage(),
129 | ...
130 | new RNPaystackPackage() //<-- Add line
131 | );
132 | }
133 | ```
134 |
135 | #### More Config (Only applicable to Android using react-native-paystack v3.1.4+ & RN less than 0.57.0)
136 | - Update Gradle plugin to v3.0.0+ for your app, follow the following steps if you are not sure how:
137 | * Edit your `~ android/build.gradle` to look similar to [build.gradle](https://github.com/tolu360/vestarapp/blob/master/android/build.gradle)
138 | * Edit your `~ android/gradle/wrapper/gradle-wrapper.properties` to look similar to [gradle-wrapper.properties](https://github.com/tolu360/vestarapp/blob/master/android/gradle/wrapper/gradle-wrapper.properties)
139 | - To avoid build issues, enable `Aapt2` for your project by adding `android.enableAapt2=true` to your `android/gradle.properties` file.
140 | - If you are using RN with a version lower than 0.57.0, it is important you replace your `node-modules/react-native/react.gradle` file with [this version @ commit da6a5e0](https://github.com/facebook/react-native/blob/da6a5e0439c168147271ef66ad5ebbeebd6fce3b/react.gradle) to avoid further build issues when assembling a release version of your app.
141 |
142 | ## 3. Usage
143 |
144 | ### Initialize Library
145 | Somewhere high up in your project and way before calling any other method exposed by this library, your `index` file or equivalent is a good spot, ensure you initialize the library with your `public key` as follos:
146 |
147 | ```js
148 | import RNPaystack from 'react-native-paystack';
149 |
150 | RNPaystack.init({ publicKey: 'YOUR_PUBLIC_KEY_HERE' });
151 | ```
152 |
153 | ### Charging a Card with Access Code (iOS & Android)
154 | It's a cinch to charge a card token using the react-native-paystack module. This is the recommended or the most-preferred workflow favored by the folks at Paystack. Initiate a new transaction on your server side using the appropriate [Paystack endpoint](https://developers.paystack.co/reference#initialize-a-transaction) - obtain an `access_code` and complete the charge on your mobile application. Pls note, the SDK assumes you are responsible for building the card form/UI.
155 |
156 | ```javascript
157 | RNPaystack.chargeCardWithAccessCode(cardParams);
158 | ```
159 | To be more elaborate, `cardParams` is a Javascript `Object` representing the card to be charged and `RNPaystack.chargeCardWithAccessCode()` returns a Javascript `Promise` like:
160 |
161 | ```js
162 | import RNPaystack from 'react-native-paystack';
163 |
164 | chargeCard() {
165 |
166 | RNPaystack.chargeCardWithAccessCode({
167 | cardNumber: '4123450131001381',
168 | expiryMonth: '10',
169 | expiryYear: '17',
170 | cvc: '883',
171 | accessCode: '2p3j42th639duy4'
172 | })
173 | .then(response => {
174 | console.log(response); // do stuff with the token
175 | })
176 | .catch(error => {
177 | console.log(error); // error is a javascript Error object
178 | console.log(error.message);
179 | console.log(error.code);
180 | })
181 |
182 | }
183 | ```
184 |
185 | #### Request Signature
186 |
187 | | Argument | Type | Description |
188 | | ------------- |:-------------:| :-----|
189 | | cardNumber | string | the card number as a String without any seperator e.g 5555555555554444 |
190 | | expiryMonth | string | the card expiry month as a double-digit ranging from 1-12 e.g 10 (October) |
191 | | expiryYear | string | the card expiry year as a double-digit e.g 15 |
192 | | cvc | string | the card 3/4 digit security code as a String e.g 123 |
193 | | accessCode | string | the access_code obtained for the charge |
194 |
195 | #### Response Object
196 |
197 | An object of the form is returned from a successful token request
198 |
199 | ```javascript
200 | {
201 | reference: "trx_1k2o600w"
202 | }
203 | ```
204 |
205 | ### Charging a Card (iOS & Android)
206 | Using the react-native-paystack module, you can start and complete a transaction with the mobile Paystack Android and iOS SDKs. With this option, you pass both your charge and card properties to the SDK - with this worklow, you initiate and complete a transaction on your mobile app. Note that as with charging with an access_code, the SDK assumes you are responsible for building the card form/UI.
207 |
208 | ```javascript
209 | RNPaystack.chargeCard(chargeParams);
210 | ```
211 | To be more elaborate, `chargeParams` is a Javascript `Object` representing the parameters of the charge to be initiated and `RNPaystack.chargeCard()` returns a Javascript `Promise` like:
212 |
213 | ```js
214 | import RNPaystack from 'react-native-paystack';
215 |
216 | chargeCard() {
217 |
218 | RNPaystack.chargeCard({
219 | cardNumber: '4123450131001381',
220 | expiryMonth: '10',
221 | expiryYear: '17',
222 | cvc: '883',
223 | email: 'chargeIOS@master.dev',
224 | amountInKobo: 150000,
225 | subAccount: 'ACCT_pz61jjjsslnx1d9',
226 | })
227 | .then(response => {
228 | console.log(response); // card charged successfully, get reference here
229 | })
230 | .catch(error => {
231 | console.log(error); // error is a javascript Error object
232 | console.log(error.message);
233 | console.log(error.code);
234 | })
235 |
236 | }
237 | ```
238 |
239 | #### Request Signature (chargeParams)
240 |
241 | | Argument | Type | Description |
242 | | ------------- |:-------------:| :-----|
243 | | cardNumber | string | the card number as a String without any seperator e.g 5555555555554444 |
244 | | expiryMonth | string | the card expiry month as a double-digit ranging from 1-12 e.g 10 (October) |
245 | | expiryYear | string | the card expiry year as a double-digit e.g 15 |
246 | | cvc | string | the card 3/4 digit security code as e.g 123 |
247 | | email | string | email of the user to be charged |
248 | | amountInKobo | integer | the transaction amount in kobo |
249 | | currency (optional) | string | sets the currency for the transaction e.g. USD |
250 | | plan (optional) | string | sets the plan ID if the transaction is to create a subscription e.g. PLN_n0p196bg73y4jcx |
251 | | subAccount (optional) | string | sets the subaccount ID for split-payment transactions e.g. ACCT_pz61jjjsslnx1d9 |
252 | | transactionCharge (optional) | integer | the amount to be charged on a split-payment, use only when `subAccount` is set |
253 | | bearer (optional) | string | sets which party bears paystack fees on a split-payment e.g. 'subaccount', use only when `subAccount` is set |
254 | | reference (optional) | string | sets the transaction reference which must be unique per transaction |
255 |
256 | #### Response Object
257 |
258 | An object of the form is returned from a successful charge
259 |
260 | ```javascript
261 | {
262 | reference: "trx_1k2o600w"
263 | }
264 | ```
265 |
266 | ### Verifying a Charge
267 | Verify a charge by calling Paystack's [REST API](https://api.paystack.co/transaction/verify) with the `reference` obtained above. An `authorization_code` will be returned once the card has been charged successfully. Learn more about that [here](https://developers.paystack.co/docs/verify-transaction).
268 |
269 | **Parameter:**
270 |
271 | - reference - the transaction reference (required)
272 |
273 | **Example**
274 |
275 | ```bash
276 | $ curl https://api.paystack.co/transaction/verify/trx_1k2o600w \
277 | -H "Authorization: Bearer SECRET_KEY" \
278 | -H "Content-Type: application/json" \
279 | -X GET
280 | ```
281 |
282 | ## 4. CREDITS
283 |
284 | Perhaps needless to say, this module leverages the [Paystack Android SDK](https://github.com/PaystackHQ/paystack-android) and the [Paystack IOS SDK](https://github.com/PaystackHQ/paystack-ios) for all the heavy liftings.
285 |
286 | ## 5. CHANGELOG
287 |
288 | + 1.0.12: Initial version supporting Android.
289 | + 1.1.1: Android library upgrade and initial iOS support.
290 | + 2.0.0: A couple of breaking changes have been introduced, see [Old Docs](./Old Docs.md) for previous documentations.
291 | + 2.0.0: Upgraded to v2.0 of the Paystack Android SDK.
292 | + 2.0.0: Unified APIs across both platforms (iOS & Android).
293 | + 2.0.0: Methods now return Javascript Promises on both platforms.
294 | + 2.1.1: Upgraded to v2.1+ of both the Paystack iOS and Android SDKs.
295 | + 2.1.1: Added support for `chargeCard` on both platforms.
296 | + 2.1.1: Added support for `subscriptions` and `split-payments`.
297 | + 3.1.0: Retired support for `getToken` on both platforms.
298 | + 3.1.0: Added support for `chargeCardWithAccessCode` on both platforms.
299 | + 3.1.0: Upgraded to v3.*+ of both the Paystack iOS and Android SDKs.
300 | + 3.1.1: Fix for breaking change in RN v0.47+
301 | * 3.1.4: Miscellaneous and dependencies update on Android.
302 | * 3.2.0: A Breaking Change - Initialize library in JS, rather than in native code.
303 | * 3.3.0: Move to a CocoaPods Flow for iOS.
304 |
305 | ## 6. License
306 |
307 | This should be [The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html). I would have to get back to you on that!
308 |
309 |
--------------------------------------------------------------------------------