# Accept card payments without webhooks Learn how to confirm a card payment on your server and handle card authentication requests. # Web > This is a Web for when platform is web. View the full page at https://docs.stripe.com/payments/accept-a-payment-synchronously?platform=web. > Stripe recommends using the newer [Payment Element](https://docs.stripe.com/payments/quickstart-checkout-sessions.md) instead of the Card Element. It allows you to accept multiple payment methods with a single Element. Learn more about [when to use the Card Element and Payment Element](https://docs.stripe.com/payments/payment-card-element-comparison.md). For a wider range of support and future proofing, use the [standard integration](https://docs.stripe.com/payments/accept-a-payment.md) for asynchronous payments. This integration waits for the returned response from the client and finalizes a payment on the server, without using *webhooks* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) or processing offline events. While it might seem simpler, this integration is difficult to scale as your business grows and has several limitations: If you’re migrating an existing Stripe integration from the Charges API, follow the [migration guide](https://docs.stripe.com/payments/payment-intents/migration.md). - **Only supports cards**—You’ll have to write more code to support ACH and popular regional payment methods separately. - **Double-charge risk**—By synchronously creating a new PaymentIntent each time your customer attempts to pay, you risk accidentally double-charging your customer. Be sure to follow [best practices](https://docs.stripe.com/error-low-level.md#idempotency). - **Extra trip to client**—​​Cards with 3D Secure or those that are subject to regulations such as *Strong Customer Authentication* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase) require extra steps on the client. ​ Keep these limitations in mind if you decide to use this integration. Otherwise, use the [standard integration](https://docs.stripe.com/payments/accept-a-payment.md). ## Set up Stripe First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register). Use our official libraries for access to the Stripe API from your application: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ## Collect card details [Client-side] Collect card information on the client with Stripe.js and Stripe Elements. Elements is a set of prebuilt UI components for collecting and validating card number, postal code, and expiration date. A Stripe Element contains an iframe that securely sends the payment information to Stripe over an HTTPS connection. The checkout page address must also start with https:// rather than http:// for your integration to work. You can test your integration without using HTTPS. [Enable it](https://docs.stripe.com/security/guide.md#tls) when you’re ready to accept live payments. #### HTML + JS Include the [Stripe.js](https://docs.stripe.com/js.md) script in the head of every page on your site. Elements is automatically available as a feature of Stripe.js. ```html ``` Including the script on every page of your site lets you take advantage of Stripe’s [advanced fraud functionality](https://docs.stripe.com/radar.md) and ability to detect anomalous browsing behavior. ### Build the payment form To securely collect card details from your customers, Elements creates UI components for you that are hosted by Stripe. They’re then placed into your payment form as an iframe. To determine where to insert these components, create empty DOM elements (containers) with unique IDs within your payment form. #### HTML ```html
``` Next, create an instance of the [Stripe object](https://docs.stripe.com/js.md#stripe-function), providing your publishable [API key](https://docs.stripe.com/keys.md) as the first parameter. Afterwards, create an instance of the [Elements object](https://docs.stripe.com/js.md#stripe-elements) and use it to [mount](https://docs.stripe.com/js.md#element-mount) a Card element in the relevant placeholder in the page. ```javascript const stripe = Stripe('<>'); const elements = stripe.elements(); // Set up Stripe.js and Elements to use in checkout form const style = { base: { color: "#32325d", fontFamily: '"Helvetica Neue", Helvetica, sans-serif', fontSmoothing: "antialiased", fontSize: "16px", "::placeholder": { color: "#aab7c4" } }, invalid: { color: "#fa755a", iconColor: "#fa755a" }, }; const cardElement = elements.create('card', {style}); cardElement.mount('#card-element'); ``` The `card` Element simplifies the form and minimizes the number of fields required by inserting a single, flexible input field that securely collects all necessary card details. Otherwise, combine `cardNumber`, `cardExpiry`, and `cardCvc` Elements for a flexible, multi-input card form. > Always collect a postal code to increase card acceptance rates and reduce fraud. > > The [single line Card Element](https://docs.stripe.com/js/element/other_element?type=card) automatically collects and sends the customer’s postal code to Stripe. If you build your payment form with split Elements ([Card Number](https://docs.stripe.com/js/element/other_element?type=cardNumber), [Expiry](https://docs.stripe.com/js/element/other_element?type=cardExpiry), [CVC](https://docs.stripe.com/js/element/other_element?type=cardCvc)), add a separate input field for the customer’s postal code. ### Create a PaymentMethod Finally, use [stripe.createPaymentMethod](https://docs.stripe.com/js/payment_methods/create_payment_method) on your client to collect the card details and create a [PaymentMethod](https://docs.stripe.com/api/payment_methods.md) when the user clicks the submit button. ```javascript const form = document.getElementById('payment-form'); form.addEventListener('submit', async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); const result = await stripe.createPaymentMethod({ type: 'card', card: cardElement, billing_details: { // Include any additional collected billing details. name: 'Jenny Rosen', }, }) stripePaymentMethodHandler(result); }); ``` #### React #### npm Install [React Stripe.js](https://www.npmjs.com/package/@stripe/react-stripe-js) and the [Stripe.js loader](https://www.npmjs.com/package/@stripe/stripe-js) from the npm public registry. ```bash npm install --save @stripe/react-stripe-js @stripe/stripe-js ``` #### umd We also provide a UMD build for sites that don’t use npm or modules. Include the Stripe.js script, which exports a global `Stripe` function, and the UMD build of React Stripe.js, which exports a global `ReactStripe` object. Always load the Stripe.js script directly from **js.stripe.com** to remain PCI compliant. Don’t include the script in a bundle or host a copy of it yourself. ```html ``` > The [demo in CodeSandbox](https://codesandbox.io/s/react-stripe-official-q1loc?fontsize=14&hidenavigation=1&theme=dark) lets you try out React Stripe.js without having to create a new project. ### Add Stripe.js and Elements to your page To use Element components, wrap your checkout page component in an [Elements provider](https://docs.stripe.com/sdks/stripejs-react.md#elements-provider). Call `loadStripe` with your publishable key and pass the returned `Promise` to the `Elements` provider. ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import {Elements} from '@stripe/react-stripe-js'; import {loadStripe} from '@stripe/stripe-js'; import CheckoutForm from './CheckoutForm'; // Make sure to call `loadStripe` outside of a component's render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('<>'); function App() { return ( ); }; ReactDOM.render(, document.getElementById('root')); ``` ### Add and configure a CardElement component Use individual Element components, such as `CardElement`, to build your form. #### JSX ```jsx /** * Use the CSS tab above to style your Element's container. */ import React from 'react'; import {CardElement} from '@stripe/react-stripe-js'; import './Styles.css' const CARD_ELEMENT_OPTIONS = { style: { base: { color: "#32325d", fontFamily: '"Helvetica Neue", Helvetica, sans-serif', fontSmoothing: "antialiased", fontSize: "16px", "::placeholder": { color: "#aab7c4", }, }, invalid: { color: "#fa755a", iconColor: "#fa755a", }, }, }; function CardSection() { return ( ); }; export default CardSection; ``` Elements are completely customizable. You can [style Elements](https://docs.stripe.com/js/elements_object/create_element?type=card#elements_create-options) to match the look and feel of your site, providing seamless checkout for your customers. It’s also possible to style various input states, for example when the Element has focus. The `CardElement` simplifies the form and minimizes the number of required fields by inserting a single, flexible input field that securely collects all necessary card and billing details. Otherwise, combine `CardNumberElement`, `CardExpiryElement`, and `CardCvcElement` elements for a flexible, multi-input card form. > Always collect a postal code to increase card acceptance rates and reduce fraud. > > The [single line Card Element](https://docs.stripe.com/js/element/other_element?type=card) automatically collects and sends the customer’s postal code to Stripe. If you build your payment form with split Elements ([Card Number](https://docs.stripe.com/js/element/other_element?type=cardNumber), [Expiry](https://docs.stripe.com/js/element/other_element?type=cardExpiry), [CVC](https://docs.stripe.com/js/element/other_element?type=cardCvc)), add a separate input field for the customer’s postal code. ### Create a PaymentMethod In your payment form’s submit handler, use [stripe.createPaymentMethod](https://docs.stripe.com/js/payment_methods/create_payment_method) to collect the card details and create a [PaymentMethod](https://docs.stripe.com/api/payment_methods.md). To call `stripe.createPaymentMethod` from your payment form component, use the [useStripe](https://docs.stripe.com/sdks/stripejs-react.md#usestripe-hook) and [useElements](https://docs.stripe.com/sdks/stripejs-react.md#useelements-hook) hooks. If you prefer traditional class components over hooks, you can instead use an [ElementsConsumer](https://docs.stripe.com/sdks/stripejs-react.md#elements-consumer). #### Hooks ```jsx import React from 'react'; import {useStripe, useElements, CardElement} from '@stripe/react-stripe-js'; import CardSection from './CardSection'; export default function CheckoutForm() { const stripe = useStripe(); const elements = useElements(); const handleSubmit = async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); if (!stripe || !elements) { // Stripe.js hasn't yet loaded. // Make sure to disable form submission until Stripe.js has loaded. return; }const result = await stripe.createPaymentMethod({ type: 'card', card: elements.getElement(CardElement), billing_details: { // Include any additional collected billing details. name: 'Jenny Rosen', }, }); stripePaymentMethodHandler(result); }; return (
); } ``` #### Class Components ```jsx import React from 'react'; import {ElementsConsumer, CardElement} from '@stripe/react-stripe-js'; import CardSection from './CardSection'; class CheckoutForm extends React.Component { handleSubmit = async (event) => { // We don't want to let default form submission happen here, // which would refresh the page. event.preventDefault(); const {stripe, elements} = this.props if (!stripe || !elements) { // Stripe.js hasn't yet loaded. // Make sure to disable form submission until Stripe.js has loaded. return; }const result = await stripe.createPaymentMethod({ type: 'card', card: elements.getElement(CardElement), billing_details: { // Include any additional collected billing details. name: 'Jenny Rosen', }, }); stripePaymentMethodHandler(result); }; render() { const {stripe} = this.props; return (
); } } export default function InjectedCheckoutForm() { return ( {({stripe, elements}) => ( )} ); } ``` ## Submit the PaymentMethod to your server [Client-side] If the *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) was created successfully, send its ID to your server. ```javascript const stripePaymentMethodHandler = async (result) => { if (result.error) { // Show error in payment form } else { // Otherwise send paymentMethod.id to your server (see Step 4) const res = await fetch('/pay', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ payment_method_id: result.paymentMethod.id, }), }) const paymentResponse = await res.json(); // Handle server response (see Step 4) handleServerResponse(paymentResponse); } } ``` ## Create a PaymentIntent [Server-side] Set up an endpoint on your server to receive the request. This endpoint will also be used [later](https://docs.stripe.com/payments/accept-a-payment-synchronously.md#confirm-payment) to handle cards that require an extra step of authentication. [Create a new PaymentIntent](https://docs.stripe.com/payments/payment-intents.md#creating-a-paymentintent) with the ID of the [PaymentMethod](https://docs.stripe.com/api/payment_methods/object.md) created on your client. You can *confirm* (Confirming a PaymentIntent indicates that the customer intends to pay with the current or provided payment method. Upon confirmation, the PaymentIntent attempts to initiate a payment) the PaymentIntent by setting the [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) property to true when the PaymentIntent is created or by calling [confirm](https://docs.stripe.com/api/payment_intents/confirm.md) after creation. [Separate authorization and capture](https://docs.stripe.com/payments/place-a-hold-on-a-payment-method.md) is also supported for card payments. If the payment requires additional actions such as 3D Secure authentication, the PaymentIntent’s status will be set to `requires_action`. If the payment failed, the status is set back to `requires_payment_method` and you should show an error to your user. If the payment doesn’t require any additional authentication then a charge is created and the PaymentIntent status is set to `succeeded`. > On versions of the API before [2019-02-11](https://docs.stripe.com/upgrades.md#2019-02-11), `requires_payment_method` appears as `requires_source` and `requires_action` appears as `requires_source_action`. #### curl ```bash curl https://api.stripe.com/v1/payment_intents \ -u <>: \ -d "payment_method"="{{PAYMENT_METHOD_ID}}" \ -d "amount"=1099 \ -d "currency"="usd" \ -d "confirmation_method"="manual" \ -d "confirm"="true" ``` If you want to save the card to reuse later, create a [Customer](https://docs.stripe.com/api/customers/create.md) to store the *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) and pass the following additional parameters when creating the PaymentIntent: - [customer](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer). Set to the ID of the *Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments). - [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage). Set to `off_session` to tell Stripe that you plan to reuse this PaymentMethod for *off-session payments* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information) when your customer isn’t present. Setting this property saves the PaymentMethod to the Customer after the PaymentIntent is confirmed and any required actions from the user are complete. See the code sample on [saving cards after a payment](https://github.com/stripe-samples/saving-card-after-payment/tree/master/without-webhooks) for more details. ## Handle any next actions [Client-side] Write code to handle situations that require your customer to intervene. A payment normally succeeds after you confirm it on the server in [step 4](https://docs.stripe.com/payments/accept-a-payment-synchronously.md#create-payment-intent). However, when the PaymentIntent requires additional action from the customer, such as authenticating with *3D Secure* (3D Secure (3DS) provides an additional layer of authentication for credit card transactions that protects businesses from liability for fraudulent card payments), this code comes into play. Use [stripe.handleCardAction](https://docs.stripe.com/js/payment_intents/handle_card_action) to trigger the UI for handling customer action. If authentication succeeds, the PaymentIntent has a status of `requires_confirmation`. Confirm the PaymentIntent again on your server to finish the payment. While testing, use a [test card number](https://docs.stripe.com/testing.md#regulatory-cards) that requires authentication (for example, 4000002760003184) to force this flow. Using a card that doesn’t require authentication (for example, 4242424242424242) skips this part of the flow and completes at step 4. ```javascript const handleServerResponse = async (response) => { if (response.error) { // Show error from server on payment form } else if (response.requires_action) { // Use Stripe.js to handle the required card action const { error: errorAction, paymentIntent } = await stripe.handleCardAction(response.payment_intent_client_secret); if (errorAction) { // Show error from Stripe.js in payment form } else { // The card action has been handled // The PaymentIntent can be confirmed again on the server const serverResponse = await fetch('/pay', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ payment_intent_id: paymentIntent.id }) }); handleServerResponse(await serverResponse.json()); } } else { // Show success message } } ``` > `stripe.handleCardAction` might take several seconds to complete. During that time, disable your form from being resubmitted and show a waiting indicator like a spinner. If you receive an error, show it to the customer, re-enable the form, and hide the waiting indicator. If the customer must perform additional steps to complete the payment, such as authentication, Stripe.js walks them through that process. ## Confirm the PaymentIntent again [Server-side] This code is only executed when a payment requires additional authentication—just like the handling in the previous step. The code itself isn’t optional because any payment could require this extra step. Using the same endpoint you set up [above](https://docs.stripe.com/payments/accept-a-payment-synchronously.md#create-payment-intent), *confirm* (Confirming an intent indicates that the customer intends to use the current or provided payment method. Upon confirmation, the intent attempts to initiate the portions of the flow that have real-world side effects) the PaymentIntent again to finalize the payment and *fulfill* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected) the order. Make sure this confirmation happens within one hour of the payment attempt. Otherwise, the payment fails and transitions back to `requires_payment_method`. #### curl ```bash curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \ -u <>: \ -X "POST" ``` ## Test the integration ​​Several test cards are available for you to use in a sandbox to make sure this integration is ready. Use them with any CVC and an expiration date in the future. | Number | Description | | ---------------- | ----------------------------------------------------------------------------------------- | | 4242424242424242 | Succeeds and immediately processes the payment. | | 4000002500003155 | Requires authentication. Stripe triggers a modal asking for the customer to authenticate. | | 4000000000009995 | Always fails with a decline code of `insufficient_funds`. | For the full list of test cards see our guide on [testing](https://docs.stripe.com/testing.md). ## Optional: Recollect a CVC When creating subsequent payments on a saved card, you might want to re-collect the CVC of the card as an additional fraud measure to verify the user. Start by [listing](https://docs.stripe.com/api/payment_methods/list.md) the payment methods associated with your *Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) to determine which of them to show for CVC re-collection. On your client, use the `cardCvc` Element to re-collect a CVC value from your user for one of their payment methods, and then tokenize the CVC data using [stripe.createToken](https://docs.stripe.com/js/tokens/create_token?type=cvc_update). After sending the CVC token to your server, create a PaymentIntent on your server with the amount, currency, and the CVC token in the `payment_method_options[card][cvc_token]` parameter. As with all other tokens, you can’t use CVC tokens more than once, so each PaymentIntent should use its own unique CVC token. ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d payment_method={{PAYMENT_METHOD_ID}} \ -d customer={{CUSTOMER_ID}} \ -d amount=1099 \ -d currency=usd \ -d confirmation_method=manual \ -d confirm=true \ -d "payment_method_options[card][cvc_token]={{CVC_TOKEN_ID}}" ``` A payment might succeed even with a failed CVC check. To prevent this, configure your [Radar rules](https://docs.stripe.com/radar/rules.md#traditional-bank-checks) to block payments when CVC verification fails. # iOS > This is a iOS for when platform is ios. View the full page at https://docs.stripe.com/payments/accept-a-payment-synchronously?platform=ios. For a wider range of support and future proofing, use the [standard integration](https://docs.stripe.com/payments/accept-a-payment.md) for asynchronous payments. This integration uses a single client-to-server flow to take payments, without using *webhooks* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) or processing offline events. While it might seem simpler, this integration is difficult to scale as your business grows and has several limitations: - **Only supports cards**—You’ll have to write more code to support ACH and popular regional payment methods separately. - **Double-charge risk**—By synchronously creating a new PaymentIntent each time your customer attempts to pay, you risk accidentally double-charging your customer. Be sure to follow [best practices](https://docs.stripe.com/error-low-level.md#should-retry). - **Manual authentication handling**—Cards with 3D Secure or those that are subject to regulations such as *Strong Customer Authentication* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase) require extra steps on the client. Keep these limitations in mind if you decide to use this integration. Otherwise, use the [standard integration](https://docs.stripe.com/payments/accept-a-payment.md). ## Set up Stripe [Client-side] [Server-side] First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register). ### Server-side This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### Client-side The [Stripe iOS SDK](https://github.com/stripe/stripe-ios) is open source, [fully documented](https://stripe.dev/stripe-ios/index.html), and compatible with apps supporting iOS 13 or above. #### Swift Package Manager To install the SDK, follow these steps: 1. In Xcode, select **File** > **Add Package Dependencies…** and enter `https://github.com/stripe/stripe-ios-spm` as the repository URL. 1. Select the latest version number from our [releases page](https://github.com/stripe/stripe-ios/releases). 1. Add the **StripePaymentsUI** product to the [target of your app](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app). #### CocoaPods 1. If you haven’t already, install the latest version of [CocoaPods](https://guides.cocoapods.org/using/getting-started.html). 1. If you don’t have an existing [Podfile](https://guides.cocoapods.org/syntax/podfile.html), run the following command to create one: ```bash pod init ``` 1. Add this line to your `Podfile`: ```podfile pod 'StripePaymentsUI' ``` 1. Run the following command: ```bash pod install ``` 1. Don’t forget to use the `.xcworkspace` file to open your project in Xcode, instead of the `.xcodeproj` file, from here on out. 1. In the future, to update to the latest version of the SDK, run: ```bash pod update StripePaymentsUI ``` #### Carthage 1. If you haven’t already, install the latest version of [Carthage](https://github.com/Carthage/Carthage#installing-carthage). 1. Add this line to your `Cartfile`: ```cartfile github "stripe/stripe-ios" ``` 1. Follow the [Carthage installation instructions](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos). Make sure to embed all of the required frameworks listed [here](https://github.com/stripe/stripe-ios/tree/master/StripePaymentsUI/README.md#manual-linking). 1. In the future, to update to the latest version of the SDK, run the following command: ```bash carthage update stripe-ios --platform ios ``` #### Manual Framework 1. Head to our [GitHub releases page](https://github.com/stripe/stripe-ios/releases/latest) and download and unzip **Stripe.xcframework.zip**. 1. Drag **StripePaymentsUI.xcframework** to the **Embedded Binaries** section of the **General** settings in your Xcode project. Make sure to select **Copy items if needed**. 1. Repeat step 2 for all required frameworks listed [here](https://github.com/stripe/stripe-ios/tree/master/StripePaymentsUI/README.md#manual-linking). 1. In the future, to update to the latest version of our SDK, repeat steps 1–3. > For details on the latest SDK release and past versions, see the [Releases](https://github.com/stripe/stripe-ios/releases) page on GitHub. To receive notifications when a new release is published, [watch releases](https://help.github.com/en/articles/watching-and-unwatching-releases-for-a-repository#watching-releases-for-a-repository) for the repository. Configure the SDK with your Stripe [publishable key](https://dashboard.stripe.com/test/apikeys) on app start. This enables your app to make requests to the Stripe API. #### Swift ```swift import UIKitimportStripePaymentsUI @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {StripeAPI.defaultPublishableKey = "<>" // do any other necessary launch configuration return true } } ``` > Use your [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app. ## Create your checkout screen [Client-side] Securely collect card information on the client with [STPPaymentCardTextField](https://stripe.dev/stripe-ios/stripe-payments-ui/Classes/STPPaymentCardTextField.html), a drop-in UI component provided by the SDK that collects the card number, expiration date, CVC, and postal code. ![](https://d37ugbyn3rpeym.cloudfront.net/docs/mobile/ios/card-field.mp4) Create an instance of the card component and a “Pay” button with the following code: #### Swift ```swift import UIKit import StripePaymentsUI class CheckoutViewController: UIViewController { lazy var cardTextField: STPPaymentCardTextField = { let cardTextField = STPPaymentCardTextField() return cardTextField }() lazy var payButton: UIButton = { let button = UIButton(type: .custom) button.layer.cornerRadius = 5 button.backgroundColor = .systemBlue button.titleLabel?.font = UIFont.systemFont(ofSize: 22) button.setTitle("Pay", for: .normal) button.addTarget(self, action: #selector(pay), for: .touchUpInside) return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let stackView = UIStackView(arrangedSubviews: [cardTextField, payButton]) stackView.axis = .vertical stackView.spacing = 20 stackView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(stackView) NSLayoutConstraint.activate([ stackView.leftAnchor.constraint(equalToSystemSpacingAfter: view.leftAnchor, multiplier: 2), view.rightAnchor.constraint(equalToSystemSpacingAfter: stackView.rightAnchor, multiplier: 2), stackView.topAnchor.constraint(equalToSystemSpacingBelow: view.topAnchor, multiplier: 2), ]) } @objc func pay() { // ... } } ``` Run your app, and make sure your checkout screen shows the card component and pay button. ## Collect card details [Client-side] When your customer is ready to check out, create a *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) with the details collected by the card element. #### Swift ```swift class CheckoutViewController: UIViewController { // ... @objc func pay() { // Collect card details on the client STPAPIClient.shared.createPaymentMethod(with: cardTextField.paymentMethodParams) { [weak self] paymentMethod, error in // Create PaymentMethod failed if let createError = error { self?.displayAlert(title: "Payment failed", message: createError.localizedDescription) } if let paymentMethodId = paymentMethod?.stripeId { self?.pay(withPaymentMethod: paymentMethodId) } } } func pay(withPaymentMethod paymentMethodId: String? = nil, withPaymentIntent paymentIntentId: String? = nil) { // ...continued in the next step } // ... } ``` ## Create a PaymentIntent [Client-side] [Server-side] Stripe uses a `PaymentIntent` object to represent your intent to collect payment from a customer, tracking your charge attempts and payment state changes throughout the process. ### On the server Add an endpoint that creates the PaymentIntent with the following parameters: - [payment_method_id](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method) - the id of the PaymentMethod from the previous step - [return_url](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-return_url) - include if you’ve [set up a return URL](https://docs.stripe.com/payments/3d-secure.md) - [use_stripe_sdk](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-use_stripe_sdk) - For apps that integrate with [Stripe iOS SDK v16.0.0](https://github.com/stripe/stripe-ios/blob/master/CHANGELOG.md#1600-2019-07-18) and above, the value of this parameter must be set to `true` - For apps that integrate with older SDK versions, **don’t pass this parameter** - [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) - Set this to true to *confirm* (Confirming a PaymentIntent indicates that the customer intends to pay with the current or provided payment method. Upon confirmation, the PaymentIntent attempts to initiate a payment) the PaymentIntent If you want to save the card to reuse later, create a [Customer](https://docs.stripe.com/api/customers/create.md) to store the PaymentMethod and pass the following additional parameters when creating the PaymentIntent: - [customer](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer) - set to the ID of the *Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments). - [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage) - set to `off_session` to tell Stripe that you plan to reuse this PaymentMethod for *off-session payments* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information) when your customer isn’t present. Setting this property saves the PaymentMethod to the Customer after the PaymentIntent is confirmed and any required actions from the user are complete. After the PaymentIntent is created, return the following to the client: - the PaymentIntent’s [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) - `requiresAction: true` if the PaymentIntent [status](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-status) is `requires_action` > See an [example server implementation](https://github.com/stripe-samples/accept-a-payment/tree/master/custom-payment-flow/server). ### On the client Request a `PaymentIntent` from your server. This example passes the server a list of items to determine the price. Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices. #### Swift ```swift let BackendUrl = "http://127.0.0.1:4242/" class CheckoutViewController: UIViewController { // ... func displayAlert(title: String, message: String, restartDemo: Bool = false) { // ...omitted for brevity } func pay(withPaymentMethod paymentMethodId: String? = nil, withPaymentIntent paymentIntentId: String? = nil) { // Create a PaymentIntent on the server let url = URL(string: BackendUrl + "pay")! var json: [String: Any] = [:] if let paymentMethodId = paymentMethodId { json = [ "useStripeSdk": true, "paymentMethodId": paymentMethodId, "currency": "usd", "items": [ "id": "photo_subscription" ] ] } else if let paymentIntentId = paymentIntentId { json = [ "paymentIntentId": paymentIntentId, ] } var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = try? JSONSerialization.data(withJSONObject: json) let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, requestError) in guard let response = response as? HTTPURLResponse, response.statusCode == 200, let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] else { self?.displayAlert(title: "Payment failed", message: requestError?.localizedDescription ?? "") return } let payError = json["error"] as? String let clientSecret = json["clientSecret"] as? String let requiresAction = json["requiresAction"] as? Bool // Payment failed if let payError = payError { self?.displayAlert(title: "Payment failed", message: payError) } // Payment succeeded, no additional action required else if clientSecret != nil && (requiresAction == nil || requiresAction == false) { self?.displayAlert(title: "Payment succeeded", message: clientSecret ?? "", restartDemo: true) } // Payment requires additional action else if clientSecret != nil && requiresAction == true && self != nil { // ...continued in the next step } }) task.resume() } } ``` ## Handle any next actions [Client-side] Write code to handle situations that require your customer to intervene. A normal payment succeeds after you confirm it on the server in [step 4](https://docs.stripe.com/payments/accept-a-payment-synchronously.md#ios-create-payment-intent). However, when the PaymentIntent requires additional action from the customer, such as authenticating with *3D Secure* (3D Secure (3DS) provides an additional layer of authentication for credit card transactions that protects businesses from liability for fraudulent card payments), this code comes into play. In these cases, the PaymentIntent’s status is set to `requires_action`. On the client, pass the PaymentIntent ID to [STPPaymentHandler handleNextActionForPayment](https://stripe.dev/stripe-ios/stripe-payments/Classes/STPPaymentHandler.html#/c:@M@StripePayments@objc\(cs\)STPPaymentHandler\(im\)handleNextActionForPayment:withAuthenticationContext:returnURL:completion:). `STPPaymentHandler` presents view controllers, using the [STPAuthenticationContext](https://stripe.dev/stripe-ios/stripe-payments/Protocols/STPAuthenticationContext.html) passed in, and walks the customer through authentication. For more details, read about [3D Secure authentication on iOS](https://docs.stripe.com/payments/3d-secure.md?platform=ios). After handling required actions on the client, the status of the PaymentIntent changes to `requires_confirmation`. This step enables your integration to synchronously *fulfill* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected) the order on your backend and return the fulfillment result to your client. Send the PaymentIntent ID to your backend and confirm it again within one hour to finalize the payment. Otherwise, the payment attempt fails and transitions back to `requires_payment_method`. #### Swift ```swift class CheckoutViewController: UIViewController { // ... // Create or confirm a PaymentIntent on the server func pay(withPaymentMethod paymentMethodId: String? = nil, withPaymentIntent paymentIntentId: String? = nil) { // ... // Payment requires additional action else if clientSecret != nil && requiresAction == true && self != nil { let paymentHandler = STPPaymentHandler.shared() paymentHandler.handleNextAction(forPayment: clientSecret!, with: self!, returnURL: nil) { status, paymentIntent, handleActionError in switch (status) { case .failed: self?.displayAlert(title: "Payment failed", message: handleActionError?.localizedDescription ?? "") break case .canceled: self?.displayAlert(title: "Payment canceled", message: handleActionError?.localizedDescription ?? "") break case .succeeded: if let paymentIntent = paymentIntent, paymentIntent.status == STPPaymentIntentStatus.requiresConfirmation { print("Re-confirming PaymentIntent after handling action") self?.pay(withPaymentIntent: paymentIntent.stripeId) } else { self?.displayAlert(title: "Payment succeeded", message: paymentIntent?.description ?? "", restartDemo: true) } break @unknown default: fatalError() break } } } // ... } } extension CheckoutViewController: STPAuthenticationContext { func authenticationPresentingViewController() -> UIViewController { return self } } ``` ## Test the integration ​​Several test cards are available for you to use in a sandbox to make sure this integration is ready. Use them with any CVC and an expiration date in the future. | Number | Description | | ---------------- | ----------------------------------------------------------------------------------------- | | 4242424242424242 | Succeeds and immediately processes the payment. | | 4000002500003155 | Requires authentication. Stripe triggers a modal asking for the customer to authenticate. | | 4000000000009995 | Always fails with a decline code of `insufficient_funds`. | For the full list of test cards see our guide on [testing](https://docs.stripe.com/testing.md). ## Optional: Recollect a CVC When creating subsequent payments on a saved card, you might want to re-collect the CVC of the card as an additional fraud measure to verify the user. Start by [listing](https://docs.stripe.com/api/payment_methods/list.md) the payment methods associated with your *Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) to determine which of them to show for CVC re-collection. After re-collecting the customer’s CVC information, tokenize the CVC data using [STPAPIClient createTokenForCVCUpdate](https://stripe.dev/stripe-ios/stripe-payments/Classes/STPAPIClient.html#/c:@CM@StripePayments@StripeCore@objc\(cs\)STPAPIClient\(im\)createTokenForCVCUpdate:completion:). #### Swift ```swift import UIKit import StripePaymentsUI class CheckoutViewController: UIViewController { // ... func tokenizeCVC() { guard let cvc = cvc else { return; } STPAPIClient.shared.createToken(forCVCUpdate: cvc) { (token, error) in if error != nil || token == nil { // handle error } else { let tokenId = token?.tokenId // pass the token ID to your backend } } } // ... } ``` After sending the CVC token to your server, create a PaymentIntent on your server with the amount, currency, and the CVC token in the `payment_method_options[card][cvc_token]` parameter. ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d payment_method={{PAYMENT_METHOD_ID}} \ -d customer={{CUSTOMER_ID}} \ -d amount=1099 \ -d currency=usd \ -d confirmation_method=manual \ -d confirm=true \ -d "payment_method_options[card][cvc_token]={{CVC_TOKEN_ID}}" ``` A payment might succeed even with a failed CVC check. To prevent this, configure your [Radar rules](https://docs.stripe.com/radar/rules.md#traditional-bank-checks) to block payments when CVC verification fails. ## See also - [Accept Apple Pay](https://docs.stripe.com/apple-pay.md) # Android > This is a Android for when platform is android. View the full page at https://docs.stripe.com/payments/accept-a-payment-synchronously?platform=android. For a wider range of support and future proofing, use the [standard integration](https://docs.stripe.com/payments/accept-a-payment.md) for asynchronous payments. This integration uses a single client-to-server flow to take payments, without using *webhooks* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) or processing offline events. While it might seem simpler, this integration is difficult to scale as your business grows and has several limitations: - **Only supports cards**—You’ll have to write more code to support ACH and popular regional payment methods separately. - **Double-charge risk**—By synchronously creating a new PaymentIntent each time your customer attempts to pay, you risk accidentally double-charging your customer. Be sure to follow [best practices](https://docs.stripe.com/error-low-level.md#should-retry). - **Manual authentication handling**—Cards with 3D Secure or those that are subject to regulations such as *Strong Customer Authentication* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase) require extra steps on the client. Keep these limitations in mind if you decide to use this integration. Otherwise, use the [standard integration](https://docs.stripe.com/payments/accept-a-payment.md). ## Set up Stripe [Client-side] [Server-side] First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register). ### Server-side This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### Client-side The [Stripe Android SDK](https://github.com/stripe/stripe-android) is open source and [fully documented](https://stripe.dev/stripe-android/). To install the SDK, add `stripe-android` to the `dependencies` block of your [app/build.gradle](https://developer.android.com/studio/build/dependencies) file: #### Kotlin ```kotlin plugins { id("com.android.application") } android { ... } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:23.1.0") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:23.1.0") } ``` > For details on the latest SDK release and past versions, see the [Releases](https://github.com/stripe/stripe-android/releases) page on GitHub. To receive notifications when a new release is published, [watch releases for the repository](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository). Configure the SDK with your Stripe [publishable key](https://dashboard.stripe.com/apikeys) so that it can make requests to the Stripe API, such as in your `Application` subclass: #### Kotlin ```kotlin import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "<>" ) } } ``` > Use your [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app. Our code samples also use [OkHttp](https://github.com/square/okhttp) and [GSON](https://github.com/google/gson) to make HTTP requests to a server. ## Create your checkout screen [Client-side] Securely collect card information on the client with [CardInputWidget](https://stripe.dev/stripe-android/payments-core/com.stripe.android.view/-card-input-widget/index.html), a drop-in UI component provided by the SDK that collects the card number, expiration date, CVC, and postal code. ![](https://d37ugbyn3rpeym.cloudfront.net/docs/mobile/android/android-card-input-widget-with-postal.mp4) Create an instance of the card component and a “Pay” button by adding the following to your checkout page’s layout: ```xml