From 1d1a9e75db8f08c112e6d408f4c43009881fcdcb Mon Sep 17 00:00:00 2001 From: Kim Ehrenpohl Date: Mon, 8 Jul 2019 18:47:06 +0200 Subject: [PATCH] [stripe-v3] Support for handleCardSetup and SetupIntent (#36662) * Added types for handleCardSetup * Author * Fix format. Passes linting --- types/stripe-v3/index.d.ts | 254 +++++++++++++++++++++++++---- types/stripe-v3/stripe-v3-tests.ts | 68 +++++--- 2 files changed, 265 insertions(+), 57 deletions(-) diff --git a/types/stripe-v3/index.d.ts b/types/stripe-v3/index.d.ts index d0f54ce727..3aa0607bdd 100644 --- a/types/stripe-v3/index.d.ts +++ b/types/stripe-v3/index.d.ts @@ -9,6 +9,7 @@ // Stefan Langeder // Marlos Borges // Thomas Marek +// Kim Ehrenpohl // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped declare var Stripe: stripe.StripeStatic; @@ -24,7 +25,7 @@ declare namespace stripe { createToken(element: elements.Element, options?: TokenOptions): Promise; createToken(name: 'bank_account', options: BankAccountTokenOptions): Promise; createToken(name: 'pii', options: PiiTokenOptions): Promise; - createSource(element: elements.Element, options?: {owner?: OwnerInfo}): Promise; + createSource(element: elements.Element, options?: { owner?: OwnerInfo }): Promise; createSource(options: SourceOptions): Promise; retrieveSource(options: RetrieveSourceOptions): Promise; redirectToCheckout(options: StripeCheckoutOptions): Promise; @@ -49,6 +50,15 @@ declare namespace stripe { handleCardAction( clientSecret: string, ): Promise; + handleCardSetup( + clientSecret: string, + element: elements.Element, + data: HandleCardSetupOptions, + ): Promise; + handleCardSetup( + clientSecret: string, + data: HandleCardSetupOptionsWithoutElementsOptions, + ): Promise; confirmPaymentIntent( clientSecret: string, element: elements.Element, @@ -83,9 +93,9 @@ declare namespace stripe { } interface StripeOptions { - stripeAccount?: string; - betas?: string[]; - locale?: string; + stripeAccount?: string; + betas?: string[]; + locale?: string; } interface TokenOptions { @@ -205,12 +215,12 @@ declare namespace stripe { } type ErrorType = 'api_connection_error' - | 'api_error' - | 'authentication_error' - | 'card_error' - | 'idempotency_error' - | 'invalid_request_error' - | 'rate_limit_error'; + | 'api_error' + | 'authentication_error' + | 'card_error' + | 'idempotency_error' + | 'invalid_request_error' + | 'rate_limit_error'; interface Error { /** @@ -450,6 +460,26 @@ declare namespace stripe { } }; } + interface HandleCardSetupOptions { + /** + * Use this parameter to supply additional data relevant to + * the payment method, such as billing details. + */ + payment_method_data?: { + /** + * The billing details associated with the card. [Recommended] + */ + billing_details?: BillingDetails, + }; + } + interface HandleCardSetupOptionsWithoutElementsOptions extends HandleCardPaymentOptions { + /** + * Only one of payment_method_data and payment_method is required. + * Use payment_method to specify an existing PaymentMethod to use + * for this payment. + */ + payment_method?: string; + } interface ConfirmPaymentIntentOptions { /** @@ -521,6 +551,10 @@ declare namespace stripe { paymentIntent?: paymentIntents.PaymentIntent; error?: Error; } + interface SetupIntentResponse { + setupIntent?: setupIntents.SetupIntent; + error?: Error; + } // Container for all payment request related types namespace paymentRequest { @@ -590,14 +624,14 @@ declare namespace stripe { } interface StripePaymentRequest { - canMakePayment(): Promise<{applePay?: boolean} | null>; + canMakePayment(): Promise<{ applePay?: boolean } | null>; show(): void; update(options: StripePaymentRequestUpdateOptions): void; on(event: 'token', handler: (response: StripeTokenPaymentResponse) => void): void; on(event: 'source', handler: (response: StripeSourcePaymentResponse) => void): void; on(event: 'cancel', handler: () => void): void; - on(event: 'shippingaddresschange', handler: (response: {updateWith: (options: UpdateDetails) => void, shippingAddress: ShippingAddress}) => void): void; - on(event: 'shippingoptionchange', handler: (response: {updateWith: (options: UpdateDetails) => void, shippingOption: ShippingOption}) => void): void; + on(event: 'shippingaddresschange', handler: (response: { updateWith: (options: UpdateDetails) => void, shippingAddress: ShippingAddress }) => void): void; + on(event: 'shippingoptionchange', handler: (response: { updateWith: (options: UpdateDetails) => void, shippingOption: ShippingOption }) => void): void; } } @@ -615,7 +649,7 @@ declare namespace stripe { // Cannot find name 'HTMLElement' mount(domElement: any): void; on(event: eventTypes, handler: handler): void; - on(event: 'click', handler: (response: {preventDefault: () => void}) => void): void; + on(event: 'click', handler: (response: { preventDefault: () => void }) => void): void; focus(): void; blur(): void; clear(): void; @@ -718,21 +752,21 @@ declare namespace stripe { namespace paymentIntents { type PaymentIntentStatus = 'requires_payment_method' - | 'requires_confirmation' - | 'requires_action' - | 'processing' - | 'requires_capture' - | 'canceled' - | 'succeeded'; + | 'requires_confirmation' + | 'requires_action' + | 'processing' + | 'requires_capture' + | 'canceled' + | 'succeeded'; type PaymentIntentCancelationReason = 'duplicate' - | 'fraudulent' - | 'requested_by_customer' - | 'abandoned' - // Generated by Stripe internally: - | 'failed_invoice' - | 'void_invoice' - | 'automatic'; + | 'fraudulent' + | 'requested_by_customer' + | 'abandoned' + // Generated by Stripe internally: + | 'failed_invoice' + | 'void_invoice' + | 'automatic'; interface PaymentIntentNextActionRedirectToUrl { /** @@ -1324,13 +1358,13 @@ declare namespace stripe { } type paymentMethodCardBrand = 'amex' - | 'diners' - | 'discover' - | 'jcb' - | 'mastercard' - | 'unionpay' - | 'visa' - | 'unknown'; + | 'diners' + | 'discover' + | 'jcb' + | 'mastercard' + | 'unionpay' + | 'visa' + | 'unknown'; interface PaymentMethodCard { /** @@ -1537,4 +1571,156 @@ declare namespace stripe { verified_name: string; } } + + namespace setupIntents { + type SetupIntentCancelationReason = 'abandoned' + | 'requested_by_customer' + | 'duplicate'; + + type SetupIntentStatus = 'requires_payment_method' + | 'requires_confirmation' + | 'requires_action' + | 'processing' + | 'canceled' + | 'succeeded'; + + interface SetupIntentNextActionRedirectToUrl { + /** + * Type of the next action to perform + */ + type: 'redirect_to_url'; + /** + * Contains instructions for authenticating a payment by + * redirecting your customer to another page or application. + */ + redirect_to_url: { + /** + * If the customer does not exit their browser while + * authenticating, they will be redirected to this + * specified URL after completion. + */ + return_url: string; + + /** + * The URL you must redirect your customer to in + * order to authenticate. + */ + url: string; + }; + } + + interface SetupIntentNextActionUseStripeSdk { + /** + * Type of the next action to perform + */ + type: 'use_stripe_sdk'; + /** + * When confirming a SetupIntent with Stripe.js, Stripe.js depends on + * the contents of this dictionary to invoke authentication flows. The + * shape of the contents is subject to change and is only intended to + * be used by Stripe.js. + */ + use_stripe_sdk: any; + } + + interface SetupIntent { + /** + * Unique identifier for the object. + */ + id: string; + + /** + * Value is "setup_intent". + */ + object: 'setup_intent'; + + /** + * ID of the Connect application that created the SetupIntent. + */ + application: string | null; + + /** + * Reason for cancellation of this SetupIntent. + */ + cancelation_reason: SetupIntentCancelationReason | null; + + /** + * The client secret of this SetupIntent. Used for client-side retrieval using a publishable key. + * The client secret can be used to complete payment setup from your frontend. + * It should not be stored, logged, embedded in URLs, or exposed to anyone other than the customer. + * Make sure that you have TLS enabled on any page that includes the client secret. + */ + client_secret: string; + + /** + * Time at which the object was created. Measured in seconds since the Unix epoch. + */ + created: number; + + /** + * ID of the Customer this SetupIntent belongs to, if one exists. + * If present, payment methods used with this SetupIntent can only be attached + * to this Customer, and payment methods attached to other Customers cannot be + * used with this SetupIntent. + */ + customer: string | null; + + /** + * An arbitrary string attached to the object. Often useful for displaying to users. + */ + description?: string; + + /** + * The error encountered in the previous SetupIntent confirmation. + */ + last_payment_error: Error | null; + + /** + * Has the value true if the object exists in live mode or the value + * false if the object exists in test mode. + */ + livemode: boolean; + + /** + * Set of key-value pairs that you can attach to an object. This can be + * useful for storing additional information about the object in a structured format. + */ + metadata: Metadata; + + /** + * If present, this property tells you what actions you need to take in + * order for your customer to continue payment setup. + */ + next_action: SetupIntentNextActionUseStripeSdk | SetupIntentNextActionRedirectToUrl; + + /** + * The account (if any) for which the setup is intended. + */ + on_behalf_of: string | null; + + /** + * ID of the payment method used with this SetupIntent. + */ + payment_method: string | null; + + /** + * The list of payment method types (e.g. card) that this SetupIntent is allowed to set up. + */ + payment_method_types: string[]; + + /** + * Status of this SetupIntent + */ + status: SetupIntentStatus; + + /** + * Indicates how the payment method is intended to be used in the future. + * Use on_session if you intend to only reuse the payment method + * when the customer is in your checkout flow. Use off_session if your + * customer may or may not be in your checkout flow. If not provided, + * this value defaults to off_session. + */ + usage: 'on_session' | 'off_session'; + } + } } diff --git a/types/stripe-v3/stripe-v3-tests.ts b/types/stripe-v3/stripe-v3-tests.ts index 39805c5b32..9a47515e9c 100644 --- a/types/stripe-v3/stripe-v3-tests.ts +++ b/types/stripe-v3/stripe-v3-tests.ts @@ -57,9 +57,9 @@ describe("Stripe elements", () => { .then((result: stripe.TokenResponse) => { console.log(result.token); }, - (error: stripe.Error) => { - console.error(error); - }); + (error: stripe.Error) => { + console.error(error); + }); // test 3D secure const threeDSecureFrame = document.getElementById('3d-secure-frame'); @@ -138,7 +138,7 @@ describe("Stripe elements", () => { }); }); - it ("should use payment request API", () => { + it("should use payment request API", () => { const paymentRequest = stripe.paymentRequest({ country: 'US', currency: 'usd', @@ -156,28 +156,28 @@ describe("Stripe elements", () => { } }); paymentRequest.on('token', ev => { - const body = JSON.stringify({token: ev.token.id}); + const body = JSON.stringify({ token: ev.token.id }); // post to server... - Promise.resolve({ok: true}) - .then(response => { - if (response.ok) { - ev.complete('success'); - } else { - ev.complete('fail'); - } - }); + Promise.resolve({ ok: true }) + .then(response => { + if (response.ok) { + ev.complete('success'); + } else { + ev.complete('fail'); + } + }); }); paymentRequest.on('source', ev => { - const body = JSON.stringify({token: ev.source.id}); + const body = JSON.stringify({ token: ev.source.id }); // post to server... - Promise.resolve({ok: true}) - .then(response => { - if (response.ok) { - ev.complete('success'); - } else { - ev.complete('fail'); - } - }); + Promise.resolve({ ok: true }) + .then(response => { + if (response.ok) { + ev.complete('success'); + } else { + ev.complete('fail'); + } + }); }); }); @@ -199,7 +199,7 @@ describe("Stripe elements", () => { it("should use checkout API", () => { stripe.redirectToCheckout({ items: [ - {sku: 'sku_123', quantity: 1} + { sku: 'sku_123', quantity: 1 } ], successUrl: 'https://example.com/success', cancelUrl: 'https://example.com/canceled', @@ -250,4 +250,26 @@ describe("Stripe elements", () => { } }); }); + + it("should handle card setup", () => { + const card = elements.create('card'); + + stripe.handleCardSetup( + 'pi_18eYalAHEMiOZZp1l9ZTjSU0_secret_NibvRz4PMmJqjfb0sqmT7aq2', + card, + { + payment_method_data: { + billing_details: { + name: 'Jenny Rosen', + }, + } + } + ).then(result => { + if (result.error) { + console.error(result.error.message); + } else if (result.setupIntent) { + console.log(result.setupIntent.id); + } + }); + }); });