From 5de847e30026df889d05dcfa2daeb1fc93cb57ce Mon Sep 17 00:00:00 2001 From: Hugo EXTRAT Date: Tue, 7 Mar 2023 22:53:46 +0100 Subject: [PATCH 1/4] exclude example from tsconfig --- tsconfig.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 227a64b13..44222af66 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,5 +28,8 @@ "node", "jest" ] - } + }, + "exclude": [ + "example" + ] } From 8a47f4cea23c20cf91ec6e3eda28099c002a5279 Mon Sep 17 00:00:00 2001 From: Hugo EXTRAT Date: Tue, 7 Mar 2023 23:07:09 +0100 Subject: [PATCH 2/4] feat: allow custom form to make payment --- .../reactnativestripesdk/StripeSdkModule.kt | 67 +++++++++++++++++++ ios/StripeSdk.m | 5 ++ ios/StripeSdk.swift | 60 +++++++++++++++++ src/NativeStripeSdk.tsx | 3 + src/functions.ts | 10 +++ src/types/PaymentMethod.ts | 11 +++ 6 files changed, 156 insertions(+) diff --git a/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt b/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt index c49eb519e..e0633c0a7 100644 --- a/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +++ b/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt @@ -886,6 +886,73 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ return null } + /** + * Custom + */ + + private fun extractPaymentMethodCreateParams(options: ReadableMap, token: String?): PaymentMethodCreateParams { + val cardParams = getMapOrNull(options, "card") + val billingDetailsParams = getMapOrNull(options, "billingDetails") + val addressParams = getMapOrNull(billingDetailsParams, "address") + val address = mapToAddress(addressParams, null) + val billingDetails = PaymentMethod.BillingDetails.Builder() + .setAddress(address) + .setEmail(getValOr(billingDetailsParams, "email")) + .setName(getValOr(billingDetailsParams, "name")) + .setPhone(getValOr(billingDetailsParams, "phone")) + .build() + val card = if (token != null) { + PaymentMethodCreateParams.Card.create(token) + } else { + PaymentMethodCreateParams.Card.Builder() + .setCvc(cardParams?.getString("cvc")) + .setExpiryMonth(cardParams?.getInt("expMonth")) + .setExpiryYear(cardParams?.getInt("expYear")) + .setNumber(cardParams?.getString("number")) + .build() + } + return PaymentMethodCreateParams.create( + card, + billingDetails, + ) + } + + @ReactMethod + fun createPaymentMethodCustomNative(params: ReadableMap, promise: Promise) { + val billingDetailsParams = getMapOrNull(params, "billingDetails") + val addressParams = getMapOrNull(billingDetailsParams, "address") + val cardParamsMap = getMapOrNull(params, "card") + val cardParams = CardParams( + number = getValOr(cardParamsMap, "number") as String, + expMonth = cardParamsMap?.getInt("expMonth") ?: 0, + expYear = cardParamsMap?.getInt("expYear") ?: 0, + cvc = getValOr(cardParamsMap, "cvc", null) as String, + address = mapToAddress(addressParams, null), + name = getValOr(billingDetailsParams, "name"), + ) + + CoroutineScope(Dispatchers.IO).launch { + runCatching { + val token = stripe.createCardToken( + cardParams = cardParams, + stripeAccountId = stripeAccountId + ) + CoroutineScope(Dispatchers.IO).launch { + val pmcp = extractPaymentMethodCreateParams(params, token.id) + runCatching { + val paymentMethod = stripe.createPaymentMethod(pmcp) + val paymentMethodMap: WritableMap = mapFromPaymentMethod(paymentMethod) + promise.resolve(createResult("paymentMethod", paymentMethodMap)) + }.onFailure { + promise.resolve(createError("Failed", it)) + } + } + }.onFailure { + promise.resolve(createError(CreateTokenErrorType.Failed.toString(), it)) + } + } + } + companion object { const val NAME = "StripeSdk" } diff --git a/ios/StripeSdk.m b/ios/StripeSdk.m index 0bf02417c..1b5b9e786 100644 --- a/ios/StripeSdk.m +++ b/ios/StripeSdk.m @@ -193,4 +193,9 @@ @interface RCT_EXTERN_MODULE(StripeSdk, RCTEventEmitter) resolver: (RCTPromiseResolveBlock)resolve rejecter: (RCTPromiseRejectBlock)reject ) +RCT_EXTERN_METHOD( + createPaymentMethodCustomNative:(NSDictionary *)params + resolver: (RCTPromiseResolveBlock)resolve + rejecter: (RCTPromiseRejectBlock)reject + ) @end diff --git a/ios/StripeSdk.swift b/ios/StripeSdk.swift index a4390b8fe..93e80e407 100644 --- a/ios/StripeSdk.swift +++ b/ios/StripeSdk.swift @@ -1174,6 +1174,66 @@ class StripeSdk: RCTEventEmitter, STPBankSelectionViewControllerDelegate, UIAdap break } } + + // Custom + func extractPaymentMethodCreateParams( + options: NSDictionary, + token: String? + ) -> STPPaymentMethodParams { + let cardParams = options["card"] as? NSDictionary + let billingDetailsParams = options["billingDetails"] as? NSDictionary + let addressParams = billingDetailsParams?["address"] as? NSDictionary + let card = STPPaymentMethodCardParams() + let billingDetails = STPPaymentMethodBillingDetails() + let address = STPPaymentMethodAddress(address: Mappers.mapToAddress(address: addressParams)) + billingDetails.address = address + billingDetails.email = billingDetailsParams?["email"] as? String + billingDetails.name = billingDetailsParams?["name"] as? String + billingDetails.phone = billingDetailsParams?["phone"] as? String + if let token = token { + card.token = token + } else { + card.number = cardParams?["number"] as? String + card.expMonth = cardParams?["expMonth"] as? NSNumber + card.expYear = cardParams?["expYear"] as? NSNumber + card.cvc = cardParams?["cvc"] as? String + } + return STPPaymentMethodParams(card: card, billingDetails: billingDetails, metadata: nil) + } + + @objc(createPaymentMethodCustomNative:resolver:rejecter:) + func createPaymentMethodCustomNative( + params: NSDictionary, + resolver resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock + ) -> Void { + let billingDetailsParams = params["billingDetails"] as? NSDictionary + let addressParams = billingDetailsParams?["address"] as? NSDictionary + let cardParamsMap = params["card"] as? NSDictionary + let cardSourceParams = STPCardParams() + cardSourceParams.number = cardParamsMap?["number"] as? String + cardSourceParams.expMonth = UInt(truncating: cardParamsMap?["expMonth"] as? NSNumber ?? 0) + cardSourceParams.expYear = UInt(truncating: cardParamsMap?["expYear"] as? NSNumber ?? 0) + cardSourceParams.cvc = cardParamsMap?["cvc"] as? String + cardSourceParams.address = Mappers.mapToAddress(address: addressParams) + cardSourceParams.name = billingDetailsParams?["name"] as? String + STPAPIClient.shared.createToken(withCard: cardSourceParams) { token, error in + if let token = token { + let pmcp = self.extractPaymentMethodCreateParams(options: params, token: token.tokenId) + STPAPIClient.shared.createPaymentMethod(with: pmcp) { paymentMethod, error in + if let error = error { + resolve(Errors.createError(ErrorType.Failed, error as NSError)) + return + } + resolve( + Mappers.createResult("paymentMethod", Mappers.mapFromPaymentMethod(paymentMethod)) + ) + } + } else { + resolve(Errors.createError(ErrorType.Failed, error as NSError?)) + } + } + } } func findViewControllerPresenter(from uiViewController: UIViewController) -> UIViewController { diff --git a/src/NativeStripeSdk.tsx b/src/NativeStripeSdk.tsx index c1b1ac1d8..1659c11a0 100644 --- a/src/NativeStripeSdk.tsx +++ b/src/NativeStripeSdk.tsx @@ -139,6 +139,9 @@ type NativeStripeSdkType = { webServiceUrl: string, authenticationToken: string ): Promise; + createPaymentMethodCustomNative( + params: PaymentMethod.CreateParams + ): Promise; }; const { StripeSdk } = NativeModules; diff --git a/src/functions.ts b/src/functions.ts index a1ecb1602..a32191b94 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -927,3 +927,13 @@ export const openPlatformPaySetup = async (): Promise => { await NativeStripeSdk.openApplePaySetup(); } }; + +/** + * Use this method to create a payment method from a custom form. + * This include the token creation and payment method creation performed natively by stripe. + */ +export const createPaymentMethodCustom = async ( + params: PaymentMethod.CreateParams +): Promise => { + return await NativeStripeSdk.createPaymentMethodCustomNative(params); +}; diff --git a/src/types/PaymentMethod.ts b/src/types/PaymentMethod.ts index 0d7e48f59..6f5709c6c 100644 --- a/src/types/PaymentMethod.ts +++ b/src/types/PaymentMethod.ts @@ -70,6 +70,17 @@ export type CardParams = cvc?: string; billingDetails?: BillingDetails; }; + } + | { + paymentMethodType: 'Card'; + billingDetails?: BillingDetails; + card: { + number: string; + cvc: string; + expMonth: number; + expYear: number; + name: string; + }; }; export interface IdealParams { From 77754a7682d38e3b2dce47fd37063b8161482c68 Mon Sep 17 00:00:00 2001 From: Hugo EXTRAT Date: Tue, 7 Mar 2023 23:17:39 +0100 Subject: [PATCH 3/4] feat: onDidSetShippingContact event on authorization & paymentMethod --- ios/ApplePayViewController.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ios/ApplePayViewController.swift b/ios/ApplePayViewController.swift index d80ade757..4fb112b18 100644 --- a/ios/ApplePayViewController.swift +++ b/ios/ApplePayViewController.swift @@ -14,6 +14,13 @@ extension StripeSdk : PKPaymentAuthorizationViewControllerDelegate, STPApplePayC didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void ) { + if (self.hasLegacyApplePayListeners) { + // Legacy, remove when useApplePay hook is removed + let contact = payment.shippingContact + if ((contact) != nil) { + sendEvent(withName: "onDidSetShippingContact", body: ["shippingContact": Mappers.mapFromShippingContact(shippingContact: contact!)]) + } + } applePaymentMethodFlowCanBeCanceled = false if (platformPayUsesDeprecatedTokenFlow) { @@ -165,6 +172,13 @@ extension StripeSdk : PKPaymentAuthorizationViewControllerDelegate, STPApplePayC paymentInformation: PKPayment, completion: @escaping STPIntentClientSecretCompletionBlock ) { + if (self.hasLegacyApplePayListeners) { + // Legacy, remove when useApplePay hook is removed + let contact = paymentInformation.shippingContact + if ((contact) != nil) { + sendEvent(withName: "onDidSetShippingContact", body: ["shippingContact": Mappers.mapFromShippingContact(shippingContact: contact!)]) + } + } if let clientSecret = self.confirmApplePayPaymentClientSecret { completion(clientSecret, nil) } else if let clientSecret = self.confirmApplePaySetupClientSecret { From 87a97b8a434da55dcc1dae671f05e6a007d4f0e5 Mon Sep 17 00:00:00 2001 From: Romain C Date: Mon, 15 May 2023 10:53:53 +0200 Subject: [PATCH 4/4] Update mock.js Add some enums to mock.js --- jest/mock.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/jest/mock.js b/jest/mock.js index 5ba27496a..1738c43de 100644 --- a/jest/mock.js +++ b/jest/mock.js @@ -226,4 +226,17 @@ module.exports = { AddToWalletButton: () => 'AddToWalletButton', PlatformPayButton: () => 'PlatformPayButton', useStripe: jest.fn(() => mockHooks), + PlatformPay: { + ContactField: { + EmailAddress: "emailAddress", + Name: "name", + PhoneNumber: "phoneNumber", + PhoneticName: "phoneticName", + PostalAddress: "postalAddress", + }, + BillingAddressFormat: { + Full: "FULL", + Min: "MIN", + }, + }, };