# Migrate a card payments integration from the legacy Cards or Sources APIs

Learn how to migrate from using the Charges, Tokens, and Cards or Sources APIs to using the Payment Methods and Payment Intents APIs.

Use this guide to migrate your integration from creating and using the Cards or Sources APIs to the Payment Methods and Payment Intents APIs. For an overview of the main differences, see [Migrate to the Payment Intents and Payment Methods APIs](https://docs.stripe.com/payments/payment-methods/transitioning.md).

> #### Checkout Sessions API
> 
> Depending on your integration, you might be able to use the Checkout Sessions API, which requires less code than working directly with the Payment Intents API. [Compare Checkout Sessions and Payment Intents](https://docs.stripe.com/payments/checkout-sessions-and-payment-intents-comparison.md). If you migrate to the Payment Intents API, you can [migrate to Checkout Sessions later](https://docs.stripe.com/payments/payment-element/migration-ewcs.md).

## What’s changing

Stripe is ending support for creating `Card` or `Source` objects, including with the Tokens API, and for using them to make payments. If your card payments integration uses them, you must migrate to use the following APIs instead:

- Payment Methods for creating and managing card payment instruments
- Payment Intents for creating and confirming payments
- Setup Intents for saving cards without an immediate charge

### Common actions

The following table describes how some common actions differ between the legacy and current APIs:

| Action | Legacy APIs | Payment Methods/Payment Intents |
| --- | --- | --- |
| Create a card | - `POST /v1/tokens` with `card`
- `POST /v1/sources` with `type: "card"` and `usage` set to `single_use` or `reusable` | - `POST /v1/payment_methods` with `type: "card"`
- `POST /v1/payment_intents` with `setup_future_usage` (also creates a payment)
- `POST /v1/setup_intents` (doesn’t create a payment) |
| Create a one-off or recurring payment with an existing card | `POST /v1/charges` with `source` | `POST /v1/payment_intents` with `payment_method` |
| Attach a card to a customer | `POST /v1/customers/{id}/sources` | - `POST /v1/payment_intents` with `setup_future_usage` (also creates a payment)
- `POST /v1/setup_intents` (doesn’t create a payment) |
| Use 3D Secure | Not natively supported | Built into `PaymentIntents` and `SetupIntents` |
| Access a customer’s default payment method | `default_source` property of `Customer` | N/A (use dynamic payment methods or specify a payment method) |

### Migration process

The migration process includes the following steps:

1. Update your integration to create `PaymentIntents` instead of `Charges`.
   - To create a `PaymentIntent` using an existing `Card` that hasn’t yet migrated to a `PaymentMethod`, pass the `Card` ID in the `PaymentIntent`’s undocumented `source` parameter. If the `Card` is attached to a `Customer`, you also need to pass the `Customer` ID in the `customer` parameter.
   - To create a `PaymentIntent` using an existing `Source` that hasn’t yet migrated to a `PaymentMethod`, pass the `Source` ID in the `PaymentIntent`’s undocumented `source` parameter. If the `Source` is attached to a `Customer`, you also need to pass the `Customer` ID in the `customer` parameter.
   - When you create a `PaymentIntent` by passing a `PaymentMethod` ID, if the `PaymentMethod` is attached to a `Customer`, you also need to pass the `Customer` ID in the `customer` parameter.
2. Update your integration to create `PaymentMethods` instead of `Cards`.
   - If your integration doesn’t use `Customers`, update it to use them. To use a card `PaymentMethod` with a `PaymentIntent`, the card must be attached to a `Customer`.
   - Instead of creating `PaymentMethods` directly, create them through the `PaymentIntent` or `SetupIntent` flow instead. (Use a `SetupIntent` to save a card to a new `PaymentMethod` without creating a payment.)
3. Verify that your integration no longer creates `Cards` by turning on enforcement of `Card` creation blocking. If you discover any exceptions, turn off enforcement temporarily and address them.
4. Use the migration tool to migrate your existing `Cards` to `PaymentMethods`.  (Private preview)
5. Update any code that references existing `Cards` to reference the corresponding migrated `PaymentMethods` instead. This includes changing references to the `default_source` on any `Customer`, `Subscription`, or `Invoice` to instead reference its `default_payment_method`.

## Update to use the Payment Intents API

First, update your integration to use the new card payment process with `Cards` and `Sources`.

> #### Passing the Card or Source ID in the payment_method parameter
> 
> If your integration creates `PaymentIntents` or `SetupIntents` by passing the `Card` or `Source` ID in the `payment_method` parameter, you must still perform this update. You can no longer pass a `Card` or `Source` ID in `payment_method`.

### Create a payment using a Card or Source object

To charge a `Card`, pass the `Card` ID in the undocumented `source` parameter. If the `Card` is attached to a `Customer`, you must also pass the `Customer` ID in the `customer` parameter:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "source={{CARD_ID}}" \
  -d amount=2000 \
  -d currency=usd \
  -d confirm=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

Setting `confirm` to `true` attempts to create and confirm the `PaymentIntent` in a single request. If you don’t set it to true, then you need to confirm the `PaymentIntent` in a subsequent request. The `return_url` is the page to return the customer to after they authenticate a payment using a card that requires authentication on an external page. If you set `confirm` to `true`, then you need to also specify a `return_url`. Otherwise, you can specify the `return_url` when you confirm the `PaymentIntent`.

To charge a `Source`, pass the `Source` ID in the undocumented `source` parameter. If the `Source` is attached to a `Customer`, you must also pass the `Customer` ID in the `customer` parameter.

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "source={{SOURCE_ID}}" \
  -d amount=2000 \
  -d currency=usd \
  -d confirm=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

If the `Card` or `Source`is the `Customer`’s default source, you still need to include it in the `source` parameter. A `PaymentIntent` doesn’t automatically use the customer’s default source.

### Create a payment using raw card data

If your integration passes raw card data using the `card` or `source` parameter when creating a `Charge`, you can continue passing card data using the `PaymentIntent`’s undocumented `payment_method_data.card` parameter. However, Stripe doesn’t recommend handling raw card data, which requires PCI compliance. When you migrate to the Payment Methods API, use tokens or other payment method processes, such as [Stripe Elements](https://docs.stripe.com/payments/elements.md), instead.

The following example shows a one-off payment. You can’t create a reusable `Card` or `Source` by passing raw card data to a `PaymentIntent`. The instructions for migrating to the Payment Methods API explain how to save a card.

The only difference between the `card` or `source` parameter on a `Charge` and the `payment_method_data.card` parameter on a `PaymentIntent` is that you pass cardholder information to the `PaymentIntent`’s [payment_method_data.billing_details](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_data-billing_details) parameter:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=2000 \
  -d currency=usd \
  -d "payment_method_data[type]=card" \
  --data-urlencode "payment_method_data[billing_details][email]=jennyrosen@example.com" \
  -d "payment_method_data[billing_details][name]=Jenny Rosen" \
  -d "payment_method_data[card][number]=4242424242424242" \
  -d "payment_method_data[card][exp_month]=12" \
  -d "payment_method_data[card][exp_year]=2027" \
  -d "payment_method_data[card][cvc]=123" \
  -d confirm=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

### Create and authorize a payment, then capture it later

If you separate authorization and capture by setting `capture=false` when creating a `Charge`, then capturing the `Charge` later, the process for a `PaymentIntent` is similar. When you create or confirm the `PaymentIntent`, set [capture_method](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-capture_method) to `manual`:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "source={{SOURCE_ID}}" \
  -d amount=2000 \
  -d capture_method=manual \
  -d currency=usd \
  -d confirm=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

When you’re ready to capture an amount, [capture the PaymentIntent](https://docs.stripe.com/api/payment_intents/capture.md):

```curl
curl -X POST https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/capture \
  -u "<<YOUR_SECRET_KEY>>:"
```

Some of the parameters for capturing a `PaymentIntent` differ from the parameters for capturing a `Charge`. For example, `Charges` use [amount](https://docs.stripe.com/api/charges/capture.md#capture_charge-amount), and `PaymentIntents` use [amount_to_capture](https://docs.stripe.com/api/payment_intents/capture.md#capture_payment_intent-amount_to_capture) and [amount_details](https://docs.stripe.com/api/payment_intents/capture.md#capture_payment_intent-amount_details). For details, see [charge PaymentIntents](https://docs.stripe.com/api/payment_intents/capture.md).

### Handle 3D Secure authentication

When *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) triggers, the Charges API fails the payment, but a `PaymentIntent` changes to the `requires_action` status. For more information on managing 3D Secure with the Payment Intents API, see [Authenticate with 3D Secure](https://docs.stripe.com/payments/3d-secure/authentication-flow.md).

In a server-side-only integration that doesn’t interact directly with cardholders (for example, off-session payments or background billing), payments requiring cardholder authentication such as 3D Secure can hang in `requires_action` status. To prevent such payments from hanging, configure `PaymentIntents` to return an error instead of changing to `requires_action` by setting [error_on_requires_action](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-error_on_requires_action) to true when creating or confirming them. For example:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "payment_method={{PAYMENTMETHOD_ID}}" \
  -d amount=2000 \
  -d currency=usd \
  -d confirm=true \
  -d error_on_requires_action=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

### Handle PaymentIntent events

Although your integration no longer directly creates `Charge` objects, creating a `PaymentIntent` also creates a `Charge` object representing the actual payment. If you rely on `Charge` events, you can continue to do so. However, you can also listen for `PaymentIntent` events, some of which correspond directly to `Charge` events. Some common `PaymentIntent` events include:

- `payment_intent.succeeded`: Payment completed successfully
- `payment_intent.payment_failed`: Payment attempt failed
- `payment_intent.requires_action`: 3D Secure authentication is needed
- `payment_intent.requires_capture`: Authorized amount remains to be captured
- `payment_intent.amount_capturable_updated`: Authorized but uncaptured amount has changed

## Update to use the Payment Methods API

After updating your payment integration to create `PaymentIntents` instead of `Charges`, update it to create `PaymentMethods` instead of [card tokens](https://docs.stripe.com/api/tokens/create_card.md) or [sources](https://docs.stripe.com/api/sources/create.md). While you can continue to create payment methods directly, Stripe recommends using a process that avoids handling raw card data, such as [dynamic payment methods](https://docs.stripe.com/payments/payment-methods/dynamic-payment-methods.md).

You don’t need to explicitly attach an unattached `PaymentMethod` to a `Customer` before using them together with a `PaymentIntent` or `SetupIntent`. Confirming the `PaymentIntent` or `SetupIntent` automatically attaches the specified `PaymentMethod` and `Customer`. (When attaching a `PaymentMethod` using a `PaymentIntent`, you need to set the `setup_future_usage` parameter, as described later in this section.)

### Create a one-time payment without saving the card

If your existing integration creates a `Token` or `Source`, then uses it to create a `Charge` without saving the card, update it to create a `PaymentIntent` in one of the following ways:

- **Use dynamic payment methods**: (Recommended) Configure the payment method types that you want to support in your Dashboard. Then, when you create a `PaymentIntent`, set `confirm` to false (and don’t specify a `return_url`). Stripe automatically presents valid payment method options to your customer. Your integration doesn’t need to explicitly interact with the payment method:

  ```curl
  curl https://api.stripe.com/v1/payment_intents \
    -u "<<YOUR_SECRET_KEY>>:" \
    -d "automatic_payment_methods[enabled]=true" \
    -d amount=2000 \
    -d currency=usd \
    -d confirm=false
  ```

- **Specify the allowed payment method types**: Instead of configuring supported payment method types in your Dashboard, you can specify a list when creating a `PaymentIntent`. Set `confirm` to false, and Stripe automatically presents valid payment options from the list to your customer. Your integration doesn’t need to explicitly interact with the payment method:

  ```curl
  curl https://api.stripe.com/v1/payment_intents \
    -u "<<YOUR_SECRET_KEY>>:" \
    -d "allowed_payment_method_types[]=card" \
    -d "allowed_payment_method_types[]=link" \
    -d "allowed_payment_method_types[]=us_bank_account" \
    -d amount=2000 \
    -d currency=usd \
    -d confirm=false
  ```

- **Specify a payment method**: If you need to use a specific payment method, you can create a `PaymentMethod` using a token or card data, then pass it when you create a `PaymentIntent`. Use a token if possible, to avoid handling sensitive card data. If your integration passes raw card data using the `source` or `card` parameter when creating a `Charge`, you can continue passing card data using the `PaymentIntent`’s undocumented `payment_method_data.card` parameter. However, Stripe doesn’t recommend handling raw card data.

  - Use a `PaymentMethod`: (Recommended)
    1. Create the `PaymentMethod` using a token:

    ```curl
    curl https://api.stripe.com/v1/payment_methods \
      -u "<<YOUR_SECRET_KEY>>:" \
      -d type=card \
      -d "card[token]={{TOKEN_ID}}"
    ```

    1. Create the `PaymentIntent` using the `PaymentMethod`:

    ```curl
    curl https://api.stripe.com/v1/payment_intents \
      -u "<<YOUR_SECRET_KEY>>:" \
      -d "payment_method={{PAYMENTMETHOD_ID}}" \
      -d amount=2000 \
      -d currency=usd \
      -d confirm=true \
      --data-urlencode "return_url=https://www.example.com/payment_return"
    ```

  > #### Saved PaymentMethods
  > 
  > This example only works for one-off payments. If a `PaymentMethod` is already attached to a `Customer`, then you must specify that `Customer`’s ID when using that `PaymentMethod` to create a `PaymentIntent`.

  - Create the `PaymentIntent` using raw card data (remember to pass cardholder information using [payment_method_data.billing_details](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_data-billing_details), not `payment_method_data.card`):

  ```curl
  curl https://api.stripe.com/v1/payment_intents \
    -u "<<YOUR_SECRET_KEY>>:" \
    -d amount=2000 \
    -d currency=usd \
    -d "payment_method_data[type]=card" \
    --data-urlencode "payment_method_data[billing_details][email]=jennyrosen@example.com" \
    -d "payment_method_data[billing_details][name]=Jenny Rosen" \
    -d "payment_method_data[card][number]=4242424242424242" \
    -d "payment_method_data[card][exp_month]=12" \
    -d "payment_method_data[card][exp_year]=2027" \
    -d "payment_method_data[card][cvc]=123" \
    -d confirm=true \
    --data-urlencode "return_url=https://www.example.com/payment_return"
  ```

### Create a payment using a new card and save the card

To save a `PaymentMethod` for future use, you need to have a `Customer` to attach it to. When you create the `PaymentIntent`, specify the ID of the associated `Customer` and set [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage) to `on_session` or `off_session`:

- `on_session`: Future payments can only use the card when the customer is present in the checkout flow.
- `off_session`: Future payments can use the card regardless of whether the customer is present in the checkout flow. Use `off_session` to set up recurring payments, such as for a subscription.

For payments using new payment methods, Stripe recommends using dynamic payment methods or listing payment method types, as described in the previous section. When you specify a `Customer` and `setup_future_usage`, Stripe automatically generates a `PaymentMethod` based on the customer’s selection, and confirming the `PaymentIntent` attaches the `PaymentMethod` to the `Customer`. Your integration never handles raw card data. For example:

1. Create the `Customer` if they don’t already exist:
   ```curl
   curl https://api.stripe.com/v1/customers \
     -u "<<YOUR_SECRET_KEY>>:" \
     --data-urlencode "email=jennyrosen@example.com" \
     -d "name=Jenny Rosen"
   ```
2. Create the `PaymentIntent` and let Stripe manage the payment method:
   ```curl
   curl https://api.stripe.com/v1/payment_intents \
     -u "<<YOUR_SECRET_KEY>>:" \
     -d amount=2000 \
     -d "automatic_payment_methods[enabled]=true" \
     -d currency=usd \
     -d "customer={{CUSTOMER_ID}}" \
     -d confirm=false \
     -d setup_future_usage=off_session
   ```

If you need to pass raw card data, do so as described in the previous section by creating a `PaymentMethod` or by passing card data when creating the `PaymentIntent`. When saving the card, you must also specify the ID of the associated `Customer` and set [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage) to `on_session` or `off_session`. That creates the `PaymentMethod`, if needed, and confirming the `PaymentIntent` attaches the `PaymentMethod` to the `Customer`.

When you create a `PaymentMethod` using card data, pass cardholder information in the [billing_details](https://docs.stripe.com/api/payment_methods/create.md#create_payment_method-billing_details) parameter instead of the `card` parameter:

```curl
curl https://api.stripe.com/v1/payment_methods \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d type=card \
  --data-urlencode "billing_details[email]=jennyrosen@example.com" \
  -d "billing_details[name]=Jenny Rosen" \
  -d "card[number]=4242424242424242" \
  -d "card[exp_month]=12" \
  -d "card[exp_year]=2027" \
  -d "card[cvc]=123"
```

### Save or validate a new card without creating a payment

If you want to collect or validate a customer’s payment credentials without immediately taking a payment, [create a SetupIntent](https://docs.stripe.com/payments/setup-intents.md) instead of a `PaymentIntent`. The process is very similar. As with `PaymentIntents`, Stripe recommends that you use dynamic payment methods instead of directly handling card data:

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "automatic_payment_methods[enabled]=true" \
  -d confirm=false
```

You can also specify the allowed payment method types instead of managing them in your Dashboard:

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "allowed_payment_method_types[]=card" \
  -d "allowed_payment_method_types[]=link" \
  -d "allowed_payment_method_types[]=us_bank_account" \
  -d confirm=false
```

If needed, you can specify the `PaymentMethod`:

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method={{PAYMENTMETHOD_ID}}" \
  -d confirm=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

As with a `PaymentIntent`, you can’t use a `PaymentMethod` that’s attached to a different `Customer`, and confirming the `SetupIntent` attaches the `PaymentMethod` to the specified `Customer`. Also, you can no longer pass a `Card` or `Source` ID in the `payment_method` parameter. If you want to use a `Card` or `Source` that hasn’t migrated to a `PaymentMethod`, and that isn’t the `Customer`’s default source, you need to manually create a `PaymentMethod` using the corresponding `Token`.

If you need to pass raw card data when creating the `SetupIntent`, use the undocumented `payment_method_data.card` parameter and the [payment_method_data.billing_details](https://docs.stripe.com/api/setup_intents/create.md#create_setup_intent-payment_method_data-billing_details) parameter:

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method_data[type]=card" \
  --data-urlencode "payment_method_data[billing_details][email]=jennyrosen@example.com" \
  -d "payment_method_data[billing_details][name]=Jenny Rosen" \
  -d "payment_method_data[card][number]=4242424242424242" \
  -d "payment_method_data[card][exp_month]=12" \
  -d "payment_method_data[card][exp_year]=2027" \
  -d "payment_method_data[card][cvc]=123" \
  -d confirm=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

> #### 3D Secure authentication with SetupIntents
> 
> Unlike `PaymentIntents`, `SetupIntents` don’t include an `error_on_requires_action` property that causes *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) to return an error instead of changing the `status` to `requires_action`. Your integration needs to handle the `requires_action` status, even if it cancels the `SetupIntent`.

### Create a payment using a saved card

To create a `PaymentIntent` using a saved `PaymentMethod` that’s attached to a `Customer`, you need to pass both the `PaymentMethod` and `Customer` IDs:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method={{PAYMENTMETHOD_ID}}" \
  -d amount=2000 \
  -d currency=usd \
  -d confirm=true \
  --data-urlencode "return_url=https://www.example.com/payment_return"
```

If the payment method is set as the `Customer`’s [invoice_settings.default_payment_method](https://docs.stripe.com/api/customers/object.md#customer_object-invoice_settings-default_payment_method), you still need to include it in the `payment_method` parameter. The `PaymentIntent` doesn’t automatically use the default invoice payment method.

Remember that you can no longer pass a `Card` or `Source` ID in the `payment_method` parameter. To use a saved `Card` or `Source` that hasn’t migrated to a `PaymentMethod`, you need to use the undocumented `source` parameter, as described earlier. If it’s attached to a `Customer`, you need to also specify the `Customer` ID. If it isn’t attached to a `Customer`, you can set [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage) to automatically attach it to the `Customer` that you specify.

### Attach a card to a customer or detach a card from a customer

Your existing integration might create a card by passing a `Token`, `Source`, or card data when creating or updating a `Customer`. You can’t do that with `PaymentMethods`. You need to create the `PaymentMethod`, then attach it to the `Customer`. Unlike the legacy APIs, you don’t use the Sources API to attach or detach a `PaymentMethod`.

Stripe recommends using a `PaymentIntent` or `SetupIntent` to automatically attach a `PaymentMethod` to a `Customer`, as described in the previous section. Don’t use the Payment Methods API directly.

To make the `PaymentMethod` the `Customer`’s default for subscriptions and invoices, first attach it, then set its ID as their [invoice_settings.default_payment_method](https://docs.stripe.com/api/customers/update.md#update_customer-invoice_settings-default_payment_method).

```curl
curl https://api.stripe.com/v1/customers/{{CUSTOMER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "invoice_settings[default_payment_method]={{PAYMENTMETHOD_ID}}"
```

To detach a `PaymentMethod` from a `Customer`, you don’t DELETE the `PaymentMethod` like you would with a `Source`. Instead, POST a detach request to the Payment Methods API:

```curl
curl -X POST https://api.stripe.com/v1/payment_methods/{{PAYMENTMETHOD_ID}}/detach \
  -u "<<YOUR_SECRET_KEY>>:"
```

Although you don’t delete the `PaymentMethod`, detaching it is permanent and irreversible. You can’t use a detached `PaymentMethod` for payments or attach it to any `Customer`.

To list the `PaymentMethods` attached to a `Customer`, you need to use the following method of the Payment Methods API. The `Customer` object’s `sources` property doesn’t contain the attached `PaymentMethods`.

```curl
curl -G https://api.stripe.com/v1/customers/{{CUSTOMER_ID}}/payment_methods \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d type=card
```

### Manage subscriptions and invoices

Using `PaymentMethods` with `Subscriptions` and `Invoices` is similar to using `Cards` or `Sources` with them. The difference is that instead of setting the `default_source` when you create or update a `Subscription` or `Invoice`, you set `default_payment_method`. The `PaymentMethod` must be attached to the `Customer`. If both `default_source` and `default_payment_method` are set, `default_payment_method` takes precedence.

The object hierarchy overrides `PaymentMethod` precedence. When migrated objects that have both a default `Source` and a default `PaymentMethod` are involved, Stripe uses the following order of precedence:

1. `payment_method` on the `Invoice`
2. `default_payment_method` on the `Invoice`
3. `default_source` on the `Invoice`
4. `default_payment_method` on the `Subscription`
5. `default_source` on the `Subscription`
6. `invoice_settings.default_payment_method` on the `Customer`
7. `default_source` on the `Customer`

To set the [default_payment_method](https://docs.stripe.com/api/subscriptions/create.md#create_subscription-default_payment_method) on a `Subscription`:

```curl
curl https://api.stripe.com/v1/subscriptions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "default_payment_method={{PAYMENTMETHOD_ID}}" \
  -d "items[0][price]={{PRICE_ID}}"
```

To set the [default_payment_method](https://docs.stripe.com/api/invoices/create.md#create-default_payment_method) on an `Invoice`:

```curl
curl https://api.stripe.com/v1/invoices/{{INVOICE_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "default_payment_method={{PAYMENTMETHOD_ID}}"
```

You can also specify a `PaymentMethod` when paying an `Invoice`:

```curl
curl https://api.stripe.com/v1/invoices/{{INVOICE_ID}}/pay \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "payment_method={{PAYMENTMETHOD_ID}}"
```

### Handle PaymentMethod and SetupIntent events

Some common `PaymentMethod` and `SetupIntent` events you can listen for include:

- `payment_method.attached`: Successfully attached to `Customer`
- `payment_method.detached`: Successfully detached from `Customer`
- `payment_method.automatically_updated`: Network automatically updated payment method details; can be due to card expiration
- `payment_method.contact_owner`: Update suggests contacting the cardholder for updated credentials
- `setup_intent.succeeded`: Set up and attached the `PaymentMethod`
- `setup_intent.setup_failed`: `PaymentMethod` setup and attachment attempt failed
- `setup_intent.requires_action`: 3D Secure authentication is needed

### Share payment methods with connected accounts

If your *Connect* (Connect is Stripe's solution for multi-party businesses, such as marketplace or software platforms, to route payments between sellers, customers, and other recipients) platform shares `Sources` or `Cards` with connected accounts for direct charges, update your integration to instead [clone payment methods](https://docs.stripe.com/connect/direct-charges-multiple-accounts.md?accounts-namespace=v1) to connected accounts.

The main difference is that instead of creating a `Token` directly on the connected account, you call the Payment Methods API using your platform key and specifying the connected account’s ID in the `Stripe-Account` header:

```curl
curl https://api.stripe.com/v1/payment_methods \
  -u "<<YOUR_SECRET_KEY>>:" \
  -H "Stripe-Account: {{CONNECTEDACCOUNT_ID}}" \
  -d "customer={{PLATFORMCUSTOMER_ID}}" \
  -d "payment_method={{PAYMENTMETHOD_ID}}"
```

Doing so creates a clone of the `PaymentMethod` on the connected account, but not a clone of the `Customer`. The cloned `PaymentMethod` data matches the platform `PaymentMethod` data (other than its ID), but the objects aren’t connected in any way. Changes to either of them don’t affect the other.

To save and reuse the `PaymentMethod` on the connected account, create a `Customer` directly on the connected account and use a `PaymentIntent` or `SetupIntent` to attach them. The `Customer` object on the connected account has no connection to the `Customer` object on the platform, even if they contain the same information.

## Confirm full PaymentMethod integration

In your Dashboard, use [the migration tool](https://dashboard.stripe.com/workbench/health/migrations) to block the creation of new `Card` or `Source` objects, and monitor your integration for resulting errors. If any occur, determine where your integration is still attempting to create `Cards`, and update it.

## Migrate existing Cards to PaymentMethods (Private preview)

The migration tool isn’t yet generally available, but you can follow the other steps so your integration no longer creates legacy `Cards`, `Sources`, or `Charge`-only payments. Stripe will notify you when the migration tool becomes available.

## Update remaining Card references

When the object migration is complete, review your code for any references to `Source`-specific or `Card`-specific properties, and update them to instead reference the `PaymentMethod` equivalents. This includes changing any references to `default_source` on `Customers`, `Subscriptions`, or `Invoices` to instead reference `default_payment_method` (or `invoice_settings.default_payment_method`). Also make sure that your integration doesn’t set the `default_source` on any new `Customer`, `Subscription`, or `Invoice`.

For any references to specific `Card` or `Source` objects, you can use the `metadata.migrated_to` property to identify the corresponding `PaymentMethod` object.

## API parameter map

The following tables show how methods and properties used in an existing legacy Cards or Sources API integration correspond to methods and properties of an integration that uses the Payment Intents, Setup Intents, and Payment Methods APIs.

### Create-side parameters

| Method | Legacy APIs | PaymentMethods |
| --- | --- | --- |
| - PaymentIntent create or confirm
- SetupIntent create or confirm | - `source: "card_xxx"`
- `source: "src_xxx"`
- `payment_method: "card_xxx"`
- `payment_method: "src_xxx"` | `payment_method: "pm_xxx"` |
| Charge create | - `source: "tok_xxx"`
- `source: "card_xxx"`
- `source: "src_xxx"` | Use `PaymentIntent` instead |
| Customer create | - `source: "tok_xxx"`
- `source: "src_xxx"` | `payment_method: "pm_xxx"` |
| Customer update | - `source: "tok_xxx"`
- `source: "src_xxx"` | Create `PaymentMethod` and attach separately |
| Customer update | - `default_source: "tok_xxx"`
- `default_source: "card_xxx"`
- `default_source: "src_xxx"` | `invoice_settings.default_payment_method: "pm_xxx"` (only for subscription and invoice payments) |
| PaymentMethod/Customer attach | `POST /v1/customers/{id}/sources` | - `POST /v1/payment_intents` with `setup_future_usage` (also creates a payment)
- `POST /v1/setup_intents` (doesn’t create a payment) |
| PaymentMethod/Customer detach | `DELETE /v1/customers/{id}/sources/{id}` | `POST /v1/payment_methods/{id}/detach` |
| Subscription create or update | - `default_source: "tok_xxx"`
- `default_source: "card_xxx"`
- `default_source: "src_xxx"` | `default_payment_method: "pm_xxx"` |
| Invoice update | - `default_source: "tok_xxx"`
- `default_source: "card_xxx"`
- `default_source: "src_xxx"` | `default_payment_method: "pm_xxx"` |
| Invoice pay | - `default_source: "tok_xxx"`
- `default_source: "card_xxx"`
- `default_source: "src_xxx"` | `payment_method: "pm_xxx"` |

### Read-side parameters

| Object | Legacy APIs | PaymentMethods |
| --- | --- | --- |
| Retrieve customer default card | `default_source` | `invoice_settings.default_payment_method` (used only for subscription and invoice payments) |
| Retrieve subscription default card | `default_source` | `default_payment_method` |
| Retrieve invoice default card | `default_source` | `default_payment_method` |
| List customer cards | - `GET /v1/customers/{id}/sources`
- `GET /v1/customers/{id}/cards` | `GET /v1/customers/{id}/payment_methods?type=card` |
| Retrieve card | `GET /v1/customers/{id}/sources/:card_xxx` | `GET /v1/payment_methods/{id}` |
