Skip to content

Commit

Permalink
feat: allow custom form to make payment
Browse files Browse the repository at this point in the history
  • Loading branch information
huextrat committed Mar 7, 2023
1 parent 5de847e commit 8a47f4c
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 0 deletions.
67 changes: 67 additions & 0 deletions android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
5 changes: 5 additions & 0 deletions ios/StripeSdk.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
60 changes: 60 additions & 0 deletions ios/StripeSdk.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions src/NativeStripeSdk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ type NativeStripeSdkType = {
webServiceUrl: string,
authenticationToken: string
): Promise<void>;
createPaymentMethodCustomNative(
params: PaymentMethod.CreateParams
): Promise<CreatePaymentMethodResult>;
};

const { StripeSdk } = NativeModules;
Expand Down
10 changes: 10 additions & 0 deletions src/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,3 +927,13 @@ export const openPlatformPaySetup = async (): Promise<void> => {
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<CreatePaymentMethodResult> => {
return await NativeStripeSdk.createPaymentMethodCustomNative(params);
};
11 changes: 11 additions & 0 deletions src/types/PaymentMethod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 8a47f4c

Please sign in to comment.