# 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.

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.

Create an instance of the card component and a “Pay” button by adding the following to your checkout page’s layout:
```xml
```
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.
#### Kotlin
```kotlin
class CheckoutActivity : AppCompatActivity() {
private lateinit var stripe: Stripe
// ...
private fun pay() {
val weakActivity = WeakReference(this)
// Collect card details on the client
val cardInputWidget =
findViewById(R.id.cardInputWidget)
val params = cardInputWidget.paymentMethodCreateParams
if (params == null) {
return
}
// Configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API
stripe = Stripe(applicationContext, PaymentConfiguration.getInstance(applicationContext).publishableKey)
lifecycleScope.launch {
runCatching {
stripe.createPaymentMethod(params)
}.fold(
onSuccess = { result ->
// Create a PaymentIntent on the server with a PaymentMethod
print("Created PaymentMethod")
pay(result.id, null)
},
onFailure = {
displayAlert(weakActivity.get(), "Payment failed", "Error: $it")
}
)
}
}
private fun pay(paymentMethod: String?, paymentIntent: String?) {
// ...
}
}
```
## 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
- [use_stripe_sdk](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-use_stripe_sdk)
- For apps that integrate with [Stripe Android SDK v10.0.0](https://github.com/stripe/stripe-android/blob/master/CHANGELOG.md#1000---2019-07-19) 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.
#### Kotlin
```kotlin
class CheckoutActivity : AppCompatActivity() {
private val backendUrl = "http://10.0.2.2:4242/"
private val httpClient = OkHttpClient()
private fun displayAlert(activity: Activity?, title: String, message: String, restartDemo: Boolean = false) {
// omitted for brevity
}
private fun pay(paymentMethod: String?, paymentIntent: String?) {
val weakActivity = WeakReference(this)
var json = ""
if (!paymentMethod.isNullOrEmpty()) {
json = """
{
"useStripeSdk":true,
"paymentMethodId":"$paymentMethod",
"currency":"usd",
"items": [
{"id":"photo_subscription"}
]
}
"""
}
else if (!paymentIntent.isNullOrEmpty()) {
json = """
{
"paymentIntentId":"$paymentIntent"
}
"""
}
// Create a PaymentIntent on the server
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = json.toRequestBody(mediaType)
val request = Request.Builder()
.url(backendUrl + "pay")
.post(body)
.build()
httpClient.newCall(request)
.enqueue(object: Callback {
override fun onFailure(call: Call, e: IOException) {
displayAlert(weakActivity.get(), "Payment failed", "Error: $e")
}
override fun onResponse(call: Call, response: Response) {
// Request failed
if (!response.isSuccessful) {
displayAlert(weakActivity.get(), "Payment failed", "Error: $response")
} else {
val responseData = response.body?.string()
var responseJson = JSONObject(responseData)
val payError: String? = responseJson.optString("error")
val clientSecret: String? = responseJson.optString("clientSecret")
val requiresAction: Boolean? = responseJson.optBoolean("requiresAction")
// Payment failed
if (payError != null && payError.isNotEmpty()) {
displayAlert(weakActivity.get(), "Payment failed", "Error: $payError")
}
// Payment succeeded
else if ((clientSecret != null && clientSecret.isNotEmpty())
&& (requiresAction == null || requiresAction == false)) {
displayAlert(weakActivity.get(), "Payment succeeded", "$clientSecret", restartDemo = true)
}
// Payment requires additional actions
else if ((clientSecret != null && clientSecret.isNotEmpty())
&& requiresAction == true) {
runOnUiThread {
if (weakActivity.get() != null) {
// ...continued in the next step
}
}
}
}
}
})
}
}
```
## Handle any next actions [Client-side]
If the payment requires additional actions like *3D Secure authentication* (3D Secure (3DS) provides an additional layer of authentication for credit card transactions that protects businesses from liability for fraudulent card payments), the PaymentIntent’s status is set to `requires_action`. On the client, pass the `PaymentIntent` ID to [handleNextActionForPayment](https://stripe.dev/stripe-android/payments-core/com.stripe.android/-stripe/handle-next-action-for-payment.html). This SDK presents additional activities and walks the customer through authentication. See [Supporting 3D Secure Authentication on Android](https://docs.stripe.com/payments/3d-secure.md?platform=android) to learn more.
After handling a required action on the client, the status of the PaymentIntent is `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`.
#### Kotlin
```kotlin
class CheckoutActivity : AppCompatActivity() {
// ...
private fun pay(paymentMethod: String?, paymentIntent: String?) {
// ...
val activity = weakActivity.get()!!
stripe.handleNextActionForPayment(activity, clientSecret)
// ...
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val weakActivity = WeakReference(this)
// Handle the result of stripe.handleNextActionForPayment
if (stripe.isPaymentResult(requestCode, data)) {
lifecycleScope.launch {
runCatching {
stripe.getPaymentIntentResult(requestCode, data!!)
}.fold(
onSuccess = { result ->
val paymentIntent = result.intent
val status = paymentIntent.status
if (status == StripeIntent.Status.Succeeded) {
val gson = GsonBuilder().setPrettyPrinting().create()
displayAlert(
weakActivity.get(),
"Payment succeeded",
gson.toJson(paymentIntent),
restartDemo = true
)
} else if (status == StripeIntent.Status.RequiresPaymentMethod) {
// Payment failed – allow retrying using a different payment method
displayAlert(
weakActivity.get(),
"Payment failed",
paymentIntent.lastPaymentError!!.message ?: ""
)
} else if (status == StripeIntent.Status.RequiresConfirmation) {
print("Re-confirming PaymentIntent after handling a required action")
pay(null, paymentIntent.id)
} else {
displayAlert(
weakActivity.get(),
"Payment status unknown",
"unhandled status: $status",
restartDemo = true
)
}
}, onFailure = {
// Payment request failed – allow retrying using the same payment method
displayAlert(weakActivity.get(), "Payment failed", it.toString())
}
)
}
}
}
}
```
The result of payment authentication returns to your calling Activity through [Activity#onActivityResult()](https://developer.android.com/reference/android/app/Activity.html#onActivityResult\(int,%20int,%20android.content.Intent\)). Handle the result by calling [Stripe#onPaymentResult()](https://stripe.dev/stripe-android/payments-core/com.stripe.android/-stripe/on-payment-result.html) within `Activity#onActivityResult()`. The [PaymentIntentResult](https://stripe.dev/stripe-android/payments-core/com.stripe.android/-payment-intent-result/index.html) returned in [ApiResultCallback#onSuccess()](https://stripe.dev/stripe-android/payments-core/com.stripe.android/-api-result-callback/on-success.html) has two getters:
- `getIntent()` - a [PaymentIntent](https://stripe.dev/stripe-android/payments-core/com.stripe.android/-stripe-intent-result/index.html#com.stripe.android/StripeIntentResult/intent/#/PointingToDeclaration/) object retrieved after confirmation/authentication
- `getOutcome()` - a [StripeIntentResult.Status](https://stripe.dev/stripe-android/payments-core/com.stripe.android/-stripe-intent-result/index.html#com.stripe.android/StripeIntentResult/outcome/#/PointingToDeclaration/) value that indicates the outcome of payment authentication
- `SUCCEEDED` - payment authentication succeeded
- `FAILED` - payment authentication failed
- `CANCELED` - the customer canceled required payment authentication
- `TIMEDOUT` - the payment authentication attempt timed-out
## 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 [Stripe#createCvcUpdateToken()](https://stripe.dev/stripe-android/payments-core/com.stripe.android/-stripe/create-cvc-update-token.html).
#### Kotlin
```kotlin
class CheckoutActivity : AppCompatActivity() {
private val stripe: Stripe by lazy {
Stripe(this, "<>")
}
private fun tokenizeCvc(cvc: String) {
stripe.createCvcUpdateToken(
cvc,
callback = CvcUpdateResultCallback()
)
}
private class CvcUpdateResultCallback : ApiResultCallback {
override fun onSuccess(result: Token) {
val tokenId = result.id
// pass token ID to your backend
}
override fun onError(e: Exception) {
// handle error
}
}
}
```
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 Google Pay](https://docs.stripe.com/google-pay.md)
# React Native
> This is a React Native for when platform is react-native. View the full page at https://docs.stripe.com/payments/accept-a-payment-synchronously?platform=react-native.
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 [Server-side] [Client-side]
### 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 [React Native SDK](https://github.com/stripe/stripe-react-native) is open source and fully documented. Internally, it uses the [native iOS](https://github.com/stripe/stripe-ios) and [Android](https://github.com/stripe/stripe-android) SDKs. To install Stripe’s React Native SDK, run one of the following commands in your project’s directory (depending on which package manager you use):
#### yarn
```bash
yarn add @stripe/stripe-react-native
```
#### npm
```bash
npm install @stripe/stripe-react-native
```
Next, install some other necessary dependencies:
- For iOS, go to the **ios** directory and run `pod install` to ensure that you also install the required native dependencies.
- For Android, there are no more dependencies to install.
> We recommend following the [official TypeScript guide](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project) to add TypeScript support.
### Stripe initialization
To initialize Stripe in your React Native app, either wrap your payment screen with the `StripeProvider` component, or use the `initStripe` initialization method. Only the API [publishable key](https://docs.stripe.com/keys.md#obtain-api-keys) in `publishableKey` is required. The following example shows how to initialize Stripe using the `StripeProvider` component.
```jsx
import { useState, useEffect } from 'react';
import { StripeProvider } from '@stripe/stripe-react-native';
function App() {
const [publishableKey, setPublishableKey] = useState('');
const fetchPublishableKey = async () => {
const key = await fetchKey(); // fetch key from your server here
setPublishableKey(key);
};
useEffect(() => {
fetchPublishableKey();
}, []);
return (
{/* Your app code here */}
);
}
```
> Use your API [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 page [Client-side]
Securely collect card information on the client with `CardField`, a UI component provided by the SDK that collects the card number, expiration date, CVC, and postal code.

Add the `CardField` component to your payment screen to securely collect card details from your customers. Use the `onCardChange` callback to inspect non-sensitive information about the card, like the brand, and whether the details are complete.
```javascript
import { CardField, useStripe } from '@stripe/stripe-react-native';
function PaymentScreen() {
// ...
return (
{
console.log('cardDetails', cardDetails);
}}
onFocus={(focusedField) => {
console.log('focusField', focusedField);
}}
/>
);
}
```
Run your app and make sure your checkout page shows the `CardField` component.
## 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 `CardField` component.
```javascript
import { CardField, useStripe } from '@stripe/stripe-react-native';
function PaymentScreen() {
const { createPaymentMethod, handleNextAction } = useStripe();
const pay = () => {
// Gather customer billing information (for example, email)
const billingDetails: CreatePaymentMethod.BillingDetails = {
email: 'email@stripe.com',
phone: '+48888000888',
addressCity: 'Houston',
addressCountry: 'US',
addressLine1: '1459 Circle Drive',
addressLine2: 'Texas',
addressPostalCode: '77063',
};
// Create payment method
const { paymentMethod, error } = await createPaymentMethod({
paymentMethodType: 'Card',
paymentMethodData: {
billingDetails,
}
});
};
// ...
}
```
## Submit the PaymentMethod to your server [Client-side]
If the PaymentMethod was created successfully, send its ID to your server.
```javascript
// ...
const pay = () => {
// ...
// Send the PaymentMethod to your server to create a PaymentIntent
const response = await fetch(`/pay`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ paymentMethodId: paymentMethod.id }),
});
const { error, requires_action, payment_intent_client_secret } = await response.json();
if (error) {
// Error creating or confirming PaymentIntent
Alert.alert('Error', paymentIntentError);
return;
}
if (payment_intent_client_secret && !requires_action) {
// Payment succeeded
Alert.alert('Success', 'The payment was confirmed successfully!');
}
if (payment_intent_client_secret && requires_action) {
// ...continued below
}
};
// ...
```
## 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]
A normal payment succeeds after you *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) it on the server in step 4. However, some payment flows require 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).
For cases that require any next actions, the PaymentIntent’s status is `requires_action`. On the client, pass the PaymentIntent’s *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) to `handleNextAction`. The native handler presents a view and walks the customer through the authentication flow. After handling required actions on the client, the status of the PaymentIntent changes to `requires_confirmation`. This enables your integration to fulfill the order on your backend and return the *fulfillment* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected) 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`.
```javascript
// ...
const pay = () => {
// ...
// If PaymentIntent requires action, call handleNextAction
if (payment_intent_client_secret && requires_action) {
const { error, paymentIntent } = await handleNextAction(payment_intent_client_secret);
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else if (paymentIntent) {
if (
paymentIntent.status === PaymentIntents.Status.RequiresConfirmation
) {
// Confirm the PaymentIntent again on your server
const response = await fetch(`/pay`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ payment_intent_id: paymentIntent.id }),
});
const { error, success } = await response.json();
if (error) {
// Error during confirming Intent
Alert.alert('Error', error);
} else if (success) {
Alert.alert('Success', 'The payment was confirmed successfully!');
}
} else {
// Payment succedeed
Alert.alert('Success', 'The payment was confirmed successfully!');
}
}
}
};
// ...
```
## 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: Re-collect a CVC
When creating subsequent payments with 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 with `createTokenForCVCUpdate`.
```javascript
function PaymentScreen() {
// ...
const { createTokenForCVCUpdate } = useStripe();
const tokenizeCVC = async () => {
const { error, tokenId } = await createTokenForCVCUpdate();
if (error) {
// handle error
} else if (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.