Skip to content

Instantly share code, notes, and snippets.

@IjzerenHein
Created December 19, 2019 16:30
Show Gist options
  • Save IjzerenHein/587c4841284465f5d6d25655014766d5 to your computer and use it in GitHub Desktop.
Save IjzerenHein/587c4841284465f5d6d25655014766d5 to your computer and use it in GitHub Desktop.
iOS/JS code for showing the Stripe PaymentOptions ViewController in react-native
//
// StripeEx.h
// VickyParking
//
// Created by Hein Rutjes on 06/12/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#ifndef StripeEx_h
#define StripeEx_h
#import <Foundation/Foundation.h>
#import <PassKit/PassKit.h>
#import <Stripe/Stripe.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface StripeExModule : RCTEventEmitter <RCTBridgeModule, STPCustomerEphemeralKeyProvider, STPPaymentOptionsViewControllerDelegate>
//<STPCustomerEphemeralKeyProvider>
- (void)createCustomerKeyWithAPIVersion:(nonnull NSString *)apiVersion
completion:(nonnull STPJSONResponseCompletionBlock)
completion;
//</STPCustomerEphemeralKeyProvider>
@end
#endif /* StripeEx_h */
// @flow
import { NativeModules, NativeEventEmitter } from 'react-native';
const { StripeExModule } = NativeModules;
if (!StripeExModule) console.warn('StripeEx was not found');
export const stripeEx = StripeExModule;
export type StripeExPaymentOptionType = 'applePay' | 'paymentMethod' | 'paymentMethodParams';
export type StripeExPaymentMethodType = 'card' | 'ideal' | 'fpx' | 'unknown';
export type StripeExCreateCustomerKeyCallbackEvent = { apiVersion: string };
export type StripeExCreateCustomerKeyCallback = (
event: StripeExCreateCustomerKeyCallbackEvent
) => Promise<any>;
export type StripeExSelectPaymentOptionCallbackEvent = {
label: string,
type: StripeExPaymentOptionType,
paymentMethod?: {
id: string,
type: StripeExPaymentMethodType,
},
};
export type StripeExSelectPaymentOptionCallback = (
event: StripeExSelectPaymentOptionCallbackEvent
) => void;
const stripeExEventEmitter = new NativeEventEmitter(StripeExModule);
let globalCreateCustomerKeyCallback: ?StripeExCreateCustomerKeyCallback;
let globalSelectPaymentOptionCallback: ?StripeExSelectPaymentOptionCallback;
stripeExEventEmitter.addListener(
'CreateCustomerKey',
async (event: StripeExCreateCustomerKeyCallbackEvent) => {
try {
if (!globalCreateCustomerKeyCallback) throw new Error('No CreateCustomerKey callback');
const key = await globalCreateCustomerKeyCallback(event);
stripeEx.onCreatedCustomerKey(key, null);
} catch (err) {
stripeEx.onCreatedCustomerKey({}, { name: err.name, message: err.message });
}
}
);
stripeExEventEmitter.addListener(
'SelectPaymentOption',
(event: StripeExSelectPaymentOptionCallbackEvent) => {
if (!globalSelectPaymentOptionCallback) throw new Error('No SelectPaymentOption callback');
globalSelectPaymentOptionCallback(event);
}
);
stripeEx.showPaymentOptions = async function(
options: any,
createCustomerKeyCallback: StripeExCreateCustomerKeyCallback,
selectPaymentOptionCallback: StripeExSelectPaymentOptionCallback
): Promise<boolean> {
if (globalCreateCustomerKeyCallback) {
throw new Error('Already in progress');
}
try {
globalCreateCustomerKeyCallback = createCustomerKeyCallback;
globalSelectPaymentOptionCallback = selectPaymentOptionCallback;
const res = await stripeEx.paymentOptionsForm(options);
globalCreateCustomerKeyCallback = undefined;
globalSelectPaymentOptionCallback = undefined;
return res;
} catch (err) {
globalCreateCustomerKeyCallback = undefined;
globalSelectPaymentOptionCallback = undefined;
throw err;
}
};
//
// StripeEx.m
// VickyParking
//
// Created by Hein Rutjes on 06/12/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#import "StripeEx.h"
#import "StripeHelpers.h"
#import <React/RCTConvert.h>
@implementation StripeExModule {
NSString *_publishableKey;
NSString *_merchantId;
RCTPromiseResolveBlock _paymentOptionsFormResolver;
RCTPromiseRejectBlock _paymentOptionsFormRejecter;
STPJSONResponseCompletionBlock _createCustomerKeyCompletionBlock;
}
- (instancetype)init {
if ((self = [super init])) {
}
return self;
}
- (dispatch_queue_t)methodQueue {
// This makes sure our code is thread safe by never simultaneously executing
// Possibly useful, possibly undesirable for performance.
return dispatch_get_main_queue();
}
- (NSDictionary *)constantsToExport
{
return @{};
}
RCT_EXPORT_MODULE();
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"CreateCustomerKey",@"SelectPaymentOption"];
}
RCT_EXPORT_METHOD(init:(NSDictionary *)options) {
_publishableKey = options[@"publishableKey"];
_merchantId = options[@"merchantId"];
//[Stripe setDefaultPublishableKey:publishableKey];
}
RCT_EXPORT_METHOD(onCreatedCustomerKey:(NSDictionary *)json error:(NSDictionary *)error) {
if (_createCustomerKeyCompletionBlock == nil) return;
_createCustomerKeyCompletionBlock(json, [StripeHelpers errorFromJson:error]);
_createCustomerKeyCompletionBlock = nil;
}
- (void)createCustomerKeyWithAPIVersion:(nonnull NSString *)apiVersion
completion:(nonnull STPJSONResponseCompletionBlock)
completion
{
_createCustomerKeyCompletionBlock = completion;
[self sendEventWithName:@"CreateCustomerKey" body:@{@"apiVersion": apiVersion}];
}
/*RCT_EXPORT_METHOD(getImage:(NSString *)imageType
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
UIImage* image = nil;
if ([imageType isEqualToString:@"applePay"]) {
image = [STPImageLibrary applePayCardImage];
}
}*/
RCT_EXPORT_METHOD(paymentOptionsForm:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
if((_createCustomerKeyCompletionBlock != nil) || (_paymentOptionsFormResolver != nil)) {
reject(@"AlreadyInProgress", @"AlreadyInProgress", nil);
return;
}
// Store promise completetion handlers
_paymentOptionsFormResolver = resolve;
_paymentOptionsFormRejecter = reject;
// Create customer context
STPCustomerContext* customerContext = [[STPCustomerContext alloc]initWithKeyProvider:self];
// Get configuration
NSUInteger requiredBillingAddressFields = [StripeHelpers billingType:options[@"requiredBillingAddressFields"]];
NSString *companyName = options[@"companyName"] ? options[@"companyName"] : @"";
STPUserInformation *prefilledInformation = [StripeHelpers userInformation:options[@"prefilledInformation"]];
NSString *publishableKey = options[@"publishableKey"] ? options[@"publishableKey"] : _publishableKey;
UIModalPresentationStyle formPresentation = [StripeHelpers formPresentation:options[@"presentation"]];
STPTheme *theme = [StripeHelpers formTheme:options[@"theme"]];
STPPaymentConfiguration *configuration = [[STPPaymentConfiguration alloc] init];
[configuration setRequiredBillingAddressFields:requiredBillingAddressFields];
[configuration setCompanyName:companyName];
[configuration setPublishableKey:publishableKey];
// Create view controller
STPPaymentOptionsViewController *vc = [[STPPaymentOptionsViewController alloc] initWithConfiguration:configuration theme:theme customerContext:customerContext delegate:self];
// Display view controller
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:vc];
[navigationController setModalPresentationStyle:formPresentation];
dispatch_async(dispatch_get_main_queue(), ^{
[RCTPresentedViewController() presentViewController:navigationController animated:YES completion:nil];
});
}
- (void)paymentOptionsViewController:(nonnull STPPaymentOptionsViewController *)
paymentOptionsViewController
didFailToLoadWithError:(nonnull NSError *)error
{
[RCTPresentedViewController() dismissViewControllerAnimated:YES completion:nil];
[StripeHelpers rejectPromise:_paymentOptionsFormRejecter error:error];
_paymentOptionsFormResolver = nil;
_paymentOptionsFormRejecter = nil;
}
- (void)paymentOptionsViewControllerDidFinish:
(nonnull STPPaymentOptionsViewController *)paymentOptionsViewController
{
[RCTPresentedViewController() dismissViewControllerAnimated:YES completion:nil];
_paymentOptionsFormResolver(@YES);
_paymentOptionsFormResolver = nil;
_paymentOptionsFormRejecter = nil;
}
- (void)paymentOptionsViewControllerDidCancel:
(nonnull STPPaymentOptionsViewController *)paymentOptionsViewController
{
[RCTPresentedViewController() dismissViewControllerAnimated:YES completion:nil];
_paymentOptionsFormResolver(@NO);
_paymentOptionsFormResolver = nil;
_paymentOptionsFormRejecter = nil;
}
- (void)paymentOptionsViewController:(nonnull STPPaymentOptionsViewController *)
paymentOptionsViewController
didSelectPaymentOption:
(nonnull id<STPPaymentOption>)paymentOption
{
NSMutableDictionary* body = [[NSMutableDictionary alloc]init];
[body setValue:paymentOption.label forKey:@"label"];
[body setValue:@"unknown" forKey:@"type"];
if ([paymentOption isKindOfClass:[STPApplePayPaymentOption class]]) {
[body setValue:@"applePay" forKey:@"type"];
} else if ([paymentOption isKindOfClass:[STPPaymentMethod class]]) {
STPPaymentMethod* paymentMethod = (STPPaymentMethod*) paymentOption;
[body setValue:@"paymentMethod" forKey:@"type"];
[body setObject:@{
@"id": paymentMethod.stripeId,
@"type": [StripeHelpers string:paymentMethod.type]
} forKey:@"paymentMethod"];
} else if ([paymentOption isKindOfClass:[STPPaymentMethodParams class]]) {
STPPaymentMethodParams* paymentMethodParams = (STPPaymentMethodParams*) paymentOption;
[body setValue:@"paymentMethodParams" forKey:@"type"];
}
[self sendEventWithName:@"SelectPaymentOption" body:body];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment