├── LICENSE ├── README.md └── StripeWrapper ├── StripeAPI.js ├── StripeWrapperTests ├── Info.plist └── StripeWrapperTests.m ├── __tests__ └── stripe_api_test.js ├── iOS ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── LaunchScreen.xib ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── main.jsbundle └── main.m ├── index.ios.js ├── package.json └── stripeConfig.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Juan Gabriel Gutierrez 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Stripe API Wrapper 2 | Stripe API wrapper for react native and a credit card information form generated with tcomb-form-native. 3 | 4 | ## Set up 5 | 6 | - npm install 7 | - Modify stripeConfig.js to use your configuration to get access to Stripe API. 8 | 9 | -------------------------------------------------------------------------------- /StripeWrapper/StripeAPI.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gabriel on 15-07-15. 3 | **/ 4 | 5 | "use strict"; 6 | 7 | var superagent = require('superagent'); 8 | var base64 = require('base-64'); 9 | var utf8 = require('utf8'); 10 | 11 | 12 | module.exports = function (stripe_url, secret_key) { 13 | var module = {}; 14 | 15 | 16 | 17 | 18 | module.createCardToken = function (cardNumber , expMonth ,expYear , cvc , callback ) { 19 | 20 | 21 | var bytes = utf8.encode(secret_key+':'); 22 | var encodedSecretKey = base64.encode(bytes); 23 | 24 | try { 25 | 26 | superagent 27 | .post(stripe_url+'tokens') 28 | .set('Accept', '*/*') 29 | .set('Content-Type', 'application/x-www-form-urlencoded') 30 | .set('Authorization', 'Basic '+encodedSecretKey) 31 | .send('card'+'[number]='+cardNumber) 32 | .send('card'+'[exp_month]='+expMonth) 33 | .send('card'+'[exp_year]='+expYear) 34 | .send('card'+'[cvc]='+cvc) 35 | .end(function(err, res){ 36 | 37 | if (err) { 38 | if (!err.response) { 39 | res = { 40 | ok: false, 41 | body: { errors: { default: 'Server connection error' }} 42 | } 43 | } else { 44 | res = err.response; 45 | 46 | } 47 | } else if(!res.ok){ 48 | 49 | res = { 50 | ok: false, 51 | body: JSON.parse(res.text) 52 | } 53 | 54 | }else{ 55 | 56 | res = { 57 | ok: true, 58 | body: JSON.parse(res.text) 59 | } 60 | 61 | } 62 | 63 | console.log('response ',res.text); 64 | callback && callback(res); 65 | 66 | } 67 | ); 68 | 69 | } catch (e) { 70 | var error = {ok: false, unauthorized: false, exception: e}; 71 | console.log('error ',error ); 72 | callback && callback(error); 73 | } 74 | 75 | 76 | }; 77 | 78 | 79 | 80 | return module; 81 | }; 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /StripeWrapper/StripeWrapperTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /StripeWrapper/StripeWrapperTests/StripeWrapperTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import "RCTAssert.h" 14 | #import "RCTRedBox.h" 15 | #import "RCTRootView.h" 16 | 17 | #define TIMEOUT_SECONDS 240 18 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 19 | 20 | @interface StripeWrapperTests : XCTestCase 21 | 22 | @end 23 | 24 | @implementation StripeWrapperTests 25 | 26 | 27 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 28 | { 29 | if (test(view)) { 30 | return YES; 31 | } 32 | for (UIView *subview in [view subviews]) { 33 | if ([self findSubviewInView:subview matching:test]) { 34 | return YES; 35 | } 36 | } 37 | return NO; 38 | } 39 | 40 | - (void)testRendersWelcomeScreen { 41 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 42 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 43 | BOOL foundElement = NO; 44 | NSString *redboxError = nil; 45 | 46 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 47 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 49 | 50 | redboxError = [[RCTRedBox sharedInstance] currentErrorMessage]; 51 | 52 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 53 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 54 | return YES; 55 | } 56 | return NO; 57 | }]; 58 | } 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /StripeWrapper/__tests__/stripe_api_test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../StripeAPI'); 3 | jest.autoMockOff(); 4 | 5 | //var superagent = require('superagent'); 6 | var request = require('supertest'); 7 | var chai = require('chai'), 8 | expect = chai.expect, 9 | should = chai.should(); 10 | 11 | 12 | var config = require('./stripeConfig'); 13 | var StripeAPI = require('../StripeAPI')(config.stripeUrl,config.apiKey); 14 | 15 | var validCardToken ='tok_16RRf0DE9ZivpJtCYYBpeIWM'; 16 | 17 | 18 | var card_data = { 19 | card: { 20 | "number": 4242424242424242, 21 | "exp_month": 12, 22 | "exp_year": 2016, 23 | "cvc": 123 24 | } 25 | }; 26 | 27 | describe('StripeAPI request tokens', function() { 28 | 29 | 30 | it('call function createCardToken', function(done) { 31 | 32 | this.timeout(60000); 33 | 34 | stripe.createCardTokenTest(request, card_data.card.number, 35 | card_data.card.exp_month, 36 | card_data.card.exp_year, 37 | card_data.card.cvc, 38 | function(err, res) { 39 | 40 | if (err) 41 | return done(err); 42 | 43 | expect(res.ok).to.be.true; 44 | expect(res.body.id).to.have.length(1); 45 | done(); 46 | } 47 | 48 | ); 49 | 50 | 51 | 52 | 53 | }); 54 | }); 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /StripeWrapper/iOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /StripeWrapper/iOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTRootView.h" 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | NSURL *jsCodeLocation; 19 | 20 | /** 21 | * Loading JavaScript code - uncomment the one you want. 22 | * 23 | * OPTION 1 24 | * Load from development server. Start the server from the repository root: 25 | * 26 | * $ npm start 27 | * 28 | * To run on device, change `localhost` to the IP address of your computer 29 | * (you can get this by typing `ifconfig` into the terminal and selecting the 30 | * `inet` value under `en0:`) and make sure your computer and iOS device are 31 | * on the same Wi-Fi network. 32 | */ 33 | 34 | jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"]; 35 | 36 | /** 37 | * OPTION 2 38 | * Load from pre-bundled file on disk. To re-generate the static bundle 39 | * from the root of your project directory, run 40 | * 41 | * $ react-native bundle --minify 42 | * 43 | * see http://facebook.github.io/react-native/docs/runningondevice.html 44 | */ 45 | 46 | // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 47 | 48 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 49 | moduleName:@"StripeWrapper" 50 | launchOptions:launchOptions]; 51 | 52 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 53 | UIViewController *rootViewController = [[UIViewController alloc] init]; 54 | rootViewController.view = rootView; 55 | self.window.rootViewController = rootViewController; 56 | [self.window makeKeyAndVisible]; 57 | return YES; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /StripeWrapper/iOS/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /StripeWrapper/iOS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /StripeWrapper/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSAllowsArbitraryLoads 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /StripeWrapper/iOS/main.jsbundle: -------------------------------------------------------------------------------- 1 | // Offline JS 2 | // To re-generate the offline bundle, run this from the root of your project: 3 | // 4 | // $ react-native bundle --minify 5 | // 6 | // See http://facebook.github.io/react-native/docs/runningondevice.html for more details. 7 | 8 | throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions'); 9 | -------------------------------------------------------------------------------- /StripeWrapper/iOS/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /StripeWrapper/index.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample Stripe Wrapper 3 | * 4 | */ 5 | 'use strict'; 6 | 7 | var React = require('react-native'); 8 | var tCombForms = require('tcomb-form-native'); 9 | var config = require('./stripeConfig'); 10 | 11 | var StripeAPI = require('./StripeAPI')(config.stripeUrl,config.apiKey); 12 | 13 | var { AppRegistry, StyleSheet, Text, View, TouchableHighlight } = React; 14 | 15 | 16 | var Form = tCombForms.form.Form; 17 | 18 | // 4242424242424242,12,2016,123,this._onReceiveToken); 19 | 20 | var CreditCard = tCombForms.struct({ 21 | number: tCombForms.Num, 22 | exp_month: tCombForms.Num, 23 | exp_year: tCombForms.Num , 24 | cvc: tCombForms.Num , 25 | rememberMe: tCombForms.Bool 26 | }); 27 | 28 | var options = { 29 | auto: 'none', 30 | 31 | fields: { 32 | number: { 33 | placeholder: 'Card number', 34 | error: 'Insert a valid credit card number' 35 | }, 36 | exp_month: { 37 | placeholder: 'MM' 38 | }, 39 | exp_year: { 40 | placeholder: 'YYYY' 41 | }, 42 | cvc: { 43 | placeholder: 'CVC' 44 | } 45 | 46 | } 47 | }; 48 | 49 | 50 | 51 | 52 | var RCTStripe = React.createClass({ 53 | 54 | 55 | _onError : function(error) { 56 | 57 | console.log("error ",error); 58 | 59 | 60 | }, 61 | 62 | _onReceiveToken: function(response){ 63 | 64 | if(!response.ok){ 65 | console.log('error',response.text); 66 | }else { 67 | console.log('card token',response.body.id); 68 | alert('token:'+response.body.id); 69 | } 70 | 71 | 72 | }, 73 | 74 | onPress: function () { 75 | // call getValue() to get the values of the form 76 | var value = this.refs.form.getValue(); 77 | 78 | if (value) { // if validation fails, value will be null 79 | 80 | console.log(value); 81 | StripeAPI.createCardToken(value.number,value.exp_month,value.exp_year,value.cvc,this._onReceiveToken); 82 | 83 | } 84 | }, 85 | 86 | render: function() { 87 | return ( 88 | 89 | {/* display */} 90 |
95 | 96 | Pay $20.0 97 | 98 | 99 | ); 100 | } 101 | 102 | }); 103 | 104 | var styles = StyleSheet.create({ 105 | container: { 106 | justifyContent: 'center', 107 | marginTop: 50, 108 | padding: 20, 109 | backgroundColor: '#ffffff', 110 | }, 111 | title: { 112 | fontSize: 30, 113 | alignSelf: 'center', 114 | marginBottom: 30 115 | }, 116 | buttonText: { 117 | fontSize: 18, 118 | color: 'white', 119 | alignSelf: 'center' 120 | }, 121 | button: { 122 | height: 36, 123 | backgroundColor: '#48BBEC', 124 | borderColor: '#48BBEC', 125 | borderWidth: 1, 126 | borderRadius: 8, 127 | marginBottom: 10, 128 | alignSelf: 'stretch', 129 | justifyContent: 'center' 130 | } 131 | }); 132 | 133 | AppRegistry.registerComponent('StripeWrapper', () => RCTStripe); 134 | -------------------------------------------------------------------------------- /StripeWrapper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "StripeWrapper", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node_modules/react-native/packager/packager.sh", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "base-64": "^0.1.0", 11 | "react-native": "^0.8.0", 12 | "superagent": "^1.3.0", 13 | "tcomb-form-native": "^0.2.4", 14 | "utf8": "^2.1.1" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^5.8.12", 18 | "babel-jest": "^5.3.0", 19 | "jest-cli": "facebook/jest#0.5.x" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/llanox/react-native-stripe-wrapper" 24 | }, 25 | "author": "Juan Gabriel Gutierrez ", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/llanox/react-native-stripe-wrapper/issues" 29 | }, 30 | "jest": { 31 | "scriptPreprocessor": "/node_modules/babel-jest", 32 | "testFileExtensions": [ 33 | "es6", 34 | "js" 35 | ], 36 | "collectCoverage": true 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /StripeWrapper/stripeConfig.js: -------------------------------------------------------------------------------- 1 | var config = module.exports = {}; 2 | 3 | //StripeSettings 4 | 5 | config.stripeUrl = 'https://api.stripe.com/v1/'; 6 | config.apiKey = 'sk_test_xxxxxxxxxxxxxxxxxx'; 7 | 8 | 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------