Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add defaultValues to CardForm and CardField #1044

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
### New features

- `confirmPayment` can now be called with _just_ a client secret (e.g. `await confirmPayment("payment-intent-id")`), in other words the payment method can be excluded. If the payment method is excluded, it is assumed by the SDK that you have attached the payment method on the server-side during payment intent creation. [#1084](https://github.com/stripe/stripe-react-native/pull/1084)
- Added `defaultValues` prop to `CardField`. You can now programmatically set the card number, CVC, expiry date, and postal code. [#1044](https://github.com/stripe/stripe-react-native/pull/1044)
- Expanded `CardForm`'s `defaultValues` prop. You can now programmatically set the card number, CVC, expiry date, and postal code. [#1044](https://github.com/stripe/stripe-react-native/pull/1044)

### Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
}
}

fun setDefaultValues(defaults: ReadableMap) {
cardInputWidgetBinding.cvcEditText.setText(defaults.getString("cvc"))
cardInputWidgetBinding.cardNumberEditText.setText(defaults.getString("number"))
cardInputWidgetBinding.postalCodeEditText.setText(defaults.getString("postalCode"))
val expiryDate = defaults.getString("expiryMonth")?.plus("/").plus(defaults.getString("expiryYear"))
cardInputWidgetBinding.expiryDateEditText.setText(expiryDate)
}

fun setPlaceHolders(value: ReadableMap) {
val numberPlaceholder = getValOr(value, "number", null)
val expirationPlaceholder = getValOr(value, "expiration", null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ class CardFieldViewManager : SimpleViewManager<CardFieldView>() {
view.setPlaceHolders(placeholders)
}

@ReactProp(name = "defaultValues")
fun setDefaultValues(view: CardFieldView, defaults: ReadableMap) {
view.setDefaultValues(defaults)
}


override fun createViewInstance(reactContext: ThemedReactContext): CardFieldView {
val stripeSdkModule: StripeSdkModule? = reactContext.getNativeModule(StripeSdkModule::class.java)
val view = CardFieldView(reactContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ class CardFormView(context: ThemedReactContext) : FrameLayout(context) {

fun setDefaultValues(defaults: ReadableMap) {
setCountry(defaults.getString("countryCode"))
cardFormViewBinding.postalCode.setText(defaults.getString("postalCode"))
cardFormViewBinding.cardMultilineWidget.cvcEditText.setText(defaults.getString("cvc"))
cardFormViewBinding.cardMultilineWidget.cardNumberEditText.setText(defaults.getString("number"))
val expiryDate = defaults.getString("expiryMonth")?.plus("/").plus(defaults.getString("expiryYear"))
cardFormViewBinding.cardMultilineWidget.expiryDateEditText.setText(expiryDate)
}

private fun setCountry(countryString: String?) {
Expand Down
5 changes: 5 additions & 0 deletions example/src/screens/MultilineWebhookPaymentScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ export default function MultilineWebhookPaymentScreen() {
}}
defaultValues={{
countryCode: 'US',
number: '4242424242424242',
postalCode: '98989',
expiryMonth: '01',
expiryYear: '24',
cvc: '123',
}}
/>
<View style={styles.row}>
Expand Down
8 changes: 8 additions & 0 deletions example/src/screens/WebhookPaymentScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ export default function WebhookPaymentScreen() {
}}
cardStyle={inputStyles}
style={styles.cardField}
countryCode="US"
defaultValues={{
number: '4242424242424242',
postalCode: '98989',
expiryMonth: '01',
expiryYear: '24',
cvc: '123',
}}
/>
<View style={styles.row}>
<Switch
Expand Down
1 change: 1 addition & 0 deletions ios/CardFieldManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@interface RCT_EXTERN_MODULE(CardFieldManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(postalCodeEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(countryCode, NSString)
RCT_EXPORT_VIEW_PROPERTY(defaultValues, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(onCardChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onFocusChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(cardStyle, NSDictionary)
Expand Down
7 changes: 7 additions & 0 deletions ios/CardFieldView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate {
}
}

@objc var defaultValues: NSDictionary? {
didSet {
cardField.cardParams = Mappers.mapToCardParams(defaultValues)
cardField.postalCode = defaultValues?["postalCode"] as? String
}
}

@objc var placeholders: NSDictionary = NSDictionary() {
didSet {
if let numberPlaceholder = placeholders["number"] as? String {
Expand Down
1 change: 1 addition & 0 deletions ios/CardFormManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@interface RCT_EXTERN_MODULE(CardFormManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(onFormComplete, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(dangerouslyGetFullCardDetails, BOOL)
RCT_EXPORT_VIEW_PROPERTY(defaultValues, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(autofocus, BOOL)
RCT_EXPORT_VIEW_PROPERTY(isUserInteractionEnabledValue, BOOL)
RCT_EXTERN_METHOD(focus:(nonnull NSNumber*) reactTag)
Expand Down
20 changes: 18 additions & 2 deletions ios/CardFormView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class CardFormView: UIView, STPCardFormViewDelegate {
@objc var onFormComplete: RCTDirectEventBlock?
@objc var autofocus: Bool = false
@objc var isUserInteractionEnabledValue: Bool = true

@objc var defaultValues: NSDictionary?

override func didSetProps(_ changedProps: [String]!) {
if let cardForm = self.cardForm {
cardForm.removeFromSuperview()
Expand All @@ -29,14 +30,15 @@ class CardFormView: UIView, STPCardFormViewDelegate {
self.cardForm = _cardForm
self.addSubview(_cardForm)
setStyles()
setDefaultValues()
}

@objc var cardStyle: NSDictionary = NSDictionary() {
didSet {
setStyles()
}
}

func cardFormView(_ form: STPCardFormView, didChangeToStateComplete complete: Bool) {
if onFormComplete != nil {
let brand = STPCardValidator.brand(forNumber: cardForm?.cardParams?.card?.number ?? "")
Expand Down Expand Up @@ -100,6 +102,20 @@ class CardFormView: UIView, STPCardFormViewDelegate {
// }
}

func setDefaultValues() {
guard let cardForm = cardForm else { return }

let card = Mappers.mapToCardParams(defaultValues)

let address = STPPaymentMethodAddress()
address.postalCode = defaultValues?["postalCode"] as? String

let billingDetails = STPPaymentMethodBillingDetails()
billingDetails.address = address

cardForm.cardParams = STPPaymentMethodParams.init(card: card, billingDetails: billingDetails, metadata: nil)
}

override init(frame: CGRect) {
super.init(frame: frame)
}
Expand Down
13 changes: 13 additions & 0 deletions ios/Mappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -986,4 +986,17 @@ class Mappers {
default: return STPPaymentMethodUSBankAccountType.checking
}
}

class func mapToCardParams(_ input: NSDictionary?) -> STPPaymentMethodCardParams {
let params = STPPaymentMethodCardParams()
params.cvc = input?["cvc"] as? String
params.number = input?["number"] as? String
if let month = input?["expiryMonth"] as? String {
params.expMonth = NSNumber(value: Int(month) ?? 0)
}
if let year = input?["expiryYear"] as? String {
params.expYear = NSNumber(value: Int(year) ?? 0)
}
return params
}
}
2 changes: 2 additions & 0 deletions src/components/CardField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export interface Props extends AccessibilityProps {
postalCodeEnabled?: boolean;
/** Controls the postal code entry shown (if the postalCodeEnabled prop is set to true). Defaults to the device's default locale. */
countryCode?: string;
/** Default values which will auto-fill the CardField component. This can be especially useful for testing purposes. */
defaultValues?: CardFieldInput.DefaultCardValues;
cardStyle?: CardFieldInput.Styles;
placeholders?: CardFieldInput.Placeholders;
autofocus?: boolean;
Expand Down
13 changes: 13 additions & 0 deletions src/types/components/CardFieldInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,16 @@ export interface Methods {
blur(): void;
clear(): void;
}

export type DefaultCardValues = {
/** The month of expiry of the card. This should be a 1 or 2 digit string, e.g. "2" or "12". */
expiryMonth?: string;
/** The year of expiry of the card. This should be a 2 or 4 digit string, e.g. "25" or "2025". */
expiryYear?: string;
/** The postal code associated with the card. */
postalCode?: string;
/** The 16-digit card number (can be just a portion of it). */
number?: string;
/** The 3-digit cvc of the card. */
cvc?: string;
};
12 changes: 11 additions & 1 deletion src/types/components/CardFormView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ export interface Placeholders {
export type DefaultValues = {
/** The 2-letter country code for the country selected by default on Android. If this is null, it is set by the device's configured region in the Settings app. */
countryCode?: string;
/** The month of expiry of the card. This should be a 1 or 2 digit string, e.g. "2" or "12". */
expiryMonth?: string;
/** The year of expiry of the card. This should be a 2 or 4 digit string, e.g. "25" or "2025". */
expiryYear?: string;
/** The postal code associated with the card. */
postalCode?: string;
/** The 16-digit card number (can be just a portion of it). */
number?: string;
/** The 3-digit cvc of the card. */
cvc?: string;
};

/**
Expand All @@ -59,7 +69,7 @@ export interface NativeProps {
cardStyle?: Styles;
/** Android only */
placeholders?: Placeholders;
/** Android only */
/** Default values which will auto-fill the CardField component. This can be especially useful for testing purposes or for setting the default country. */
defaultValues?: DefaultValues;
// postalCodeEnabled: boolean;
onFocusChange(
Expand Down