├── .gitignore ├── react-native-paypal.gif ├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── br │ │ └── com │ │ └── vizir │ │ └── rn │ │ └── paypal │ │ ├── PayPalPackage.java │ │ └── PayPal.java └── build.gradle ├── index.js ├── package.json ├── .eslintrc.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /android/build 2 | /.tern-port 3 | -------------------------------------------------------------------------------- /react-native-paypal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizir/react-native-paypal/HEAD/react-native-paypal.gif -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var {PayPal} = require('react-native').NativeModules; 4 | 5 | var constants = {}; 6 | var constantNames = Object.keys(PayPal).filter(p => p == p.toUpperCase()); 7 | constantNames.forEach(c => constants[c] = PayPal[c]); 8 | 9 | var functions = { 10 | paymentRequest(payPalParameters) { 11 | return new Promise(function(resolve, reject) { 12 | PayPal.paymentRequest(payPalParameters, resolve, reject); 13 | }); 14 | } 15 | }; 16 | 17 | var exported = {}; 18 | Object.assign(exported, constants, functions); 19 | 20 | module.exports = exported; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-paypal", 3 | "version": "1.0.3", 4 | "description": "Native PayPal payment screen for React Native", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/vizir/react-native-paypal.git" 9 | }, 10 | "keywords": [ 11 | "react", 12 | "react-component", 13 | "react-native", 14 | "pay-pal", 15 | "android" 16 | ], 17 | "author": "contato@vizir.com.br", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/vizir/react-native-paypal/issues" 21 | }, 22 | "homepage": "https://github.com/vizir/react-native-paypal" 23 | } 24 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 22 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false // Set this to true to enable Proguard 16 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 17 | } 18 | } 19 | } 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | dependencies { 26 | compile 'com.facebook.react:react-native:0.18.+' 27 | compile 'com.paypal.sdk:paypal-android-sdk:2.13.0' 28 | } 29 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": [ 4 | 2, 5 | 2 6 | ], 7 | "quotes": [ 8 | 2, 9 | "single" 10 | ], 11 | "linebreak-style": [ 12 | 2, 13 | "unix" 14 | ], 15 | "semi": [ 16 | 2, 17 | "always" 18 | ] 19 | }, 20 | "env": { 21 | "es6": true, 22 | "browser": false, 23 | "node": true 24 | }, 25 | "extends": "eslint:recommended", 26 | "ecmaFeatures": { 27 | "jsx": true, 28 | "experimentalObjectRestSpread": true 29 | }, 30 | "plugins": [ 31 | "react", 32 | "react-native" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Vizir Software Studio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /android/src/main/java/br/com/vizir/rn/paypal/PayPalPackage.java: -------------------------------------------------------------------------------- 1 | package br.com.vizir.rn.paypal; 2 | 3 | import android.content.Intent; 4 | import android.content.Context; 5 | 6 | import com.facebook.react.ReactPackage; 7 | import com.facebook.react.bridge.JavaScriptModule; 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.uimanager.ViewManager; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | public class PayPalPackage implements ReactPackage { 17 | private Context context; 18 | private PayPal paypalModule; 19 | private int paymentIntentRequestCode; 20 | 21 | public PayPalPackage(Context activityContext, int paymentIntentRequestCode) { 22 | context = activityContext; 23 | this.paymentIntentRequestCode = paymentIntentRequestCode; 24 | } 25 | 26 | @Override 27 | public List createNativeModules(ReactApplicationContext reactContext) { 28 | List modules = new ArrayList<>(); 29 | paypalModule = new PayPal(reactContext, context, paymentIntentRequestCode); 30 | 31 | modules.add(paypalModule); 32 | return modules; 33 | } 34 | 35 | @Override 36 | public List> createJSModules() { 37 | return Collections.emptyList(); 38 | } 39 | 40 | @Override 41 | public List createViewManagers(ReactApplicationContext reactContext) { 42 | return Collections.emptyList(); 43 | } 44 | 45 | public void handleActivityResult(final int requestCode, final int resultCode, final Intent data) { 46 | paypalModule.handleActivityResult(requestCode, resultCode, data); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-paypal 2 | 3 | ***This project is not maintained anymore. Please have a look at https://github.com/MattFoley/react-native-paypal*** 4 | 5 | A React Native interface for the PayPal Payment UI 6 | 7 | ![Demo of a Payment using PayPal](/react-native-paypal.gif?raw=true "react-native-paypal") 8 | 9 | # Setup 10 | 11 | 1. Add react-navive-paypal to your project 12 | 13 | ``` bash 14 | npm install --save react-native-paypal 15 | ``` 16 | 17 | 2. Add the following to android/app/build.gradle 18 | 19 | ``` groovy 20 | dependencies { 21 | // ... 22 | compile project(':react-native-paypal') 23 | } 24 | ``` 25 | 26 | 3. Add the following to android/settings.gradle 27 | 28 | ``` groovy 29 | include ':react-native-paypal' 30 | project(':react-native-paypal').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-paypal/android') 31 | ``` 32 | 33 | 4. Edit android/src/.../MainActivity.java 34 | 35 | ``` java 36 | // ... 37 | import br.com.vizir.rn.paypal.PayPalPackage; // <-- 38 | import br.com.vizir.rn.paypal.PayPal; // <-- 39 | import android.content.Intent; // <-- 40 | 41 | public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler { 42 | // ... 43 | private static final int PAY_PAL_REQUEST_ID = 9; // <-- Can be any unique number 44 | private PayPalPackage payPalPackage; // <-- 45 | 46 | @Override 47 | protected void onCreate(Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | // ... 50 | payPalPackage = new PayPalPackage(this, PAY_PAL_REQUEST_ID); // <-- 51 | 52 | mReactInstanceManager = ReactInstanceManager.builder() 53 | .setApplication(getApplication()) 54 | .setBundleAssetName("index.android.bundle") 55 | .setJSMainModuleName("index.android") 56 | .addPackage(new MainReactPackage()) 57 | // ... 58 | .addPackage(payPalPackage) // <-- 59 | .setUseDeveloperSupport(BuildConfig.DEBUG) 60 | .setInitialLifecycleState(LifecycleState.RESUMED) 61 | .build(); 62 | // ... 63 | } 64 | 65 | // ... 66 | 67 | @Override 68 | public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { 69 | super.onActivityResult(requestCode, resultCode, data); 70 | if (requestCode == PAY_PAL_REQUEST_ID) { 71 | payPalPackage.handleActivityResult(requestCode, resultCode, data); // <-- 72 | } else { 73 | otherModulesHandlers(requestCode, resultCode, data); 74 | } 75 | } 76 | } 77 | ``` 78 | 79 | 5. Usage example: 80 | 81 | ```javascript 82 | var PayPal = require('react-native-paypal'); 83 | PayPalAndroid.paymentRequest({ 84 | clientId: 'AbyfNDFV53djg6w4yYgiug_JaDfBSUiYI7o6NM9HE1CQ_qk9XxbUX0nwcPXXQHaNAWYtDfphQtWB3q4R', 85 | environment: PayPalAndroid.SANDBOX, 86 | price: '42.00', 87 | currency: 'USD', 88 | description: 'PayPal Test' 89 | }).then((confirm, payment) => console.log('Paid'); /* MUST verify payment in server*/) 90 | .catch((error_code) => console.error('Failed to pay through PayPal')); 91 | ``` 92 | 93 | 6. Callback parameters: 94 | 95 | If all goes OK with the payment than the paymentRequest promise is resolved with 96 | the following arguments as JSON strings: 97 | - A confirm: 98 | ``` json 99 | { 100 | "client": { 101 | "environment": "mock", 102 | "paypal_sdk_version": "2.12.4", 103 | "platform": "Android", 104 | "product_name": "PayPal-Android-SDK" 105 | }, 106 | "response": { 107 | "create_time": "2014-02-12T22:29:49Z", 108 | "id": "PAY-6RV70583SB702805EKEYSZ6Y", 109 | "intent": "sale", 110 | "state": "approved" 111 | }, 112 | "response_type": "payment" 113 | } 114 | ``` 115 | 116 | - A payment: 117 | ```json 118 | { 119 | "amount": "1.00", 120 | "currency_code": "USD", 121 | "short_description": "PayPal Test", 122 | "intent": "sale" 123 | } 124 | ``` 125 | 126 | Handling callbacks: 127 | ```javascript 128 | PayPal.paymentRequest(...).then(function (payment, confirm) { 129 | sendPaymentToConfirmInServer(payment, confirm); 130 | }) 131 | ``` 132 | 133 | If anything fails the promise will be notify an error with a code which will be 134 | one of: 135 | - USER\_CANCELLED 136 | - INVALID\_CONFIG 137 | 138 | Handling failures: 139 | 140 | ``` javascript 141 | PayPal.paymentRequest(...).catch(function (error_code) { 142 | if (error_code == PayPal.USER_CANCELLED) { 143 | // User didn't complete the payment 144 | } else if (error_code == PayPal.INVALID_CONFIG) { 145 | // Invalid config was sent to PayPal 146 | } 147 | }) 148 | ``` 149 | 150 | ### TODO: 151 | - Automated tests 152 | - iOS version 153 | - Future payment (subscriptions) 154 | -------------------------------------------------------------------------------- /android/src/main/java/br/com/vizir/rn/paypal/PayPal.java: -------------------------------------------------------------------------------- 1 | package br.com.vizir.rn.paypal; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.facebook.react.bridge.Callback; 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 10 | import com.facebook.react.bridge.ReactMethod; 11 | import com.facebook.react.bridge.ReadableMap; 12 | 13 | import com.paypal.android.sdk.payments.PayPalAuthorization; 14 | import com.paypal.android.sdk.payments.PayPalConfiguration; 15 | import com.paypal.android.sdk.payments.PayPalPayment; 16 | import com.paypal.android.sdk.payments.PayPalService; 17 | import com.paypal.android.sdk.payments.PaymentActivity; 18 | import com.paypal.android.sdk.payments.PaymentConfirmation; 19 | 20 | import java.util.Map; 21 | import java.util.HashMap; 22 | import java.math.BigDecimal; 23 | 24 | public class PayPal extends ReactContextBaseJavaModule { 25 | private final int paymentIntentRequestCode; 26 | 27 | private static final String ERROR_USER_CANCELLED = "USER_CANCELLED"; 28 | private static final String ERROR_INVALID_CONFIG = "INVALID_CONFIG"; 29 | 30 | private Callback successCallback; 31 | private Callback errorCallback; 32 | 33 | private Context activityContext; 34 | private Activity currentActivity; 35 | 36 | public PayPal(ReactApplicationContext reactContext, Context activityContext, int requestCode) { 37 | super(reactContext); 38 | this.activityContext = activityContext; 39 | this.currentActivity = (Activity)activityContext; 40 | this.paymentIntentRequestCode = requestCode; 41 | } 42 | 43 | @Override 44 | public String getName() { 45 | return "PayPal"; 46 | } 47 | 48 | @Override public Map getConstants() { 49 | final Map constants = new HashMap<>(); 50 | 51 | constants.put("NO_NETWORK", PayPalConfiguration.ENVIRONMENT_NO_NETWORK); 52 | constants.put("SANDBOX", PayPalConfiguration.ENVIRONMENT_SANDBOX); 53 | constants.put("PRODUCTION", PayPalConfiguration.ENVIRONMENT_PRODUCTION); 54 | constants.put(ERROR_USER_CANCELLED, ERROR_USER_CANCELLED); 55 | constants.put(ERROR_INVALID_CONFIG, ERROR_INVALID_CONFIG); 56 | 57 | return constants; 58 | } 59 | 60 | @ReactMethod 61 | public void paymentRequest( 62 | final ReadableMap payPalParameters, 63 | final Callback successCallback, 64 | final Callback errorCallback 65 | ) { 66 | this.successCallback = successCallback; 67 | this.errorCallback = errorCallback; 68 | 69 | final String environment = payPalParameters.getString("environment"); 70 | final String clientId = payPalParameters.getString("clientId"); 71 | final String price = payPalParameters.getString("price"); 72 | final String currency = payPalParameters.getString("currency"); 73 | final String description = payPalParameters.getString("description"); 74 | 75 | PayPalConfiguration config = 76 | new PayPalConfiguration().environment(environment).clientId(clientId); 77 | 78 | startPayPalService(config); 79 | 80 | PayPalPayment thingToBuy = 81 | new PayPalPayment(new BigDecimal(price), currency, description, 82 | PayPalPayment.PAYMENT_INTENT_SALE); 83 | 84 | Intent intent = 85 | new Intent(activityContext, PaymentActivity.class) 86 | .putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config) 87 | .putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy); 88 | 89 | currentActivity.startActivityForResult(intent, paymentIntentRequestCode); 90 | } 91 | 92 | private void startPayPalService(PayPalConfiguration config) { 93 | Intent intent = new Intent(currentActivity, PayPalService.class); 94 | intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config); 95 | currentActivity.startService(intent); 96 | } 97 | 98 | public void handleActivityResult(final int requestCode, final int resultCode, final Intent data) { 99 | if (requestCode != paymentIntentRequestCode) { return; } 100 | 101 | if (resultCode == Activity.RESULT_OK) { 102 | PaymentConfirmation confirm = 103 | data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION); 104 | if (confirm != null) { 105 | successCallback.invoke( 106 | confirm.toJSONObject().toString(), 107 | confirm.getPayment().toJSONObject().toString() 108 | ); 109 | } 110 | } else if (resultCode == Activity.RESULT_CANCELED) { 111 | errorCallback.invoke(ERROR_USER_CANCELLED); 112 | } else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) { 113 | errorCallback.invoke(ERROR_INVALID_CONFIG); 114 | } 115 | 116 | currentActivity.stopService(new Intent(currentActivity, PayPalService.class)); 117 | } 118 | } 119 | --------------------------------------------------------------------------------