# Saved cards

Save your customer's card once, then charge it whenever you need to, with no customer present: subscriptions, top-ups, auto-debit. Your customer types their card on a Weav page, so card numbers never reach your systems.

> **Note:** Saving cards is off until Weav turns it on for your account. Weav asks for your PCI DSS evidence first. In test mode it always works, so you can build your integration now.

## 1. Save the card

Save it while your customer pays, or on its own.

### While they pay

Add `save_payment_method` and `customer.id` to a `CARD` payment:

```http
POST /v1/payments

{ "channel": "CARD", "currency": "PHP", "amount": 499.00, "external_reference_id": "SUB-USER-123-2026-10", "save_payment_method": true, "customer": { "id": "USER-123", "name": "Juan dela Cruz" }, "success_url": "https://shop.example/account/plan" }
```

Send your customer to `next_action.checkout_url`, Weav's payment page, as for any payment. There they type their card, agree that you may charge it for future payments, and confirm the payment with their bank. That one check also confirms the card: there's no ₱10.00 check.

```mermaid
sequenceDiagram
    autonumber
    actor Customer as Your customer
    participant Server as Your server
    participant Weav
    participant Bank as Their bank
    Server->>Weav: POST /v1/payments<br/>CARD, save_payment_method, customer.id
    Weav-->>Server: 201 PENDING, payment_method_id<br/>next_action.checkout_url
    Server-->>Customer: Sends them to checkout_url
    Customer->>Weav: Types their card, agrees to future charges
    Note over Customer,Weav: The card number never reaches your server
    Weav->>Bank: Charges the card
    Bank-->>Customer: Asks them to confirm it's them
    Customer->>Bank: Confirms
    alt Paid
        Weav-->>Customer: Redirects to success_url
        Weav->>Server: PAYMENT_COMPLETED webhook
        Weav->>Server: PAYMENT_METHOD_SAVED webhook<br/>the card is ACTIVE
        Note over Server: Keep the card's id (PM…)<br/>with your customer
    else Not paid
        Weav->>Server: PAYMENT_FAILED, then<br/>PAYMENT_METHOD_FAILED webhooks
        Note over Weav: The card isn't kept
    end
```

The response's `payment_method_id` is the new saved card, `PENDING` until the payment is paid. Then you get two webhooks:

- `PAYMENT_COMPLETED`, then `PAYMENT_METHOD_SAVED`: the card is `ACTIVE`. Charge it next time with its `id` (step 2).
- If the payment isn't paid, the card isn't kept: `PAYMENT_FAILED`, then `PAYMENT_METHOD_FAILED`.

A [checkout](https://docs.weavpayments.com/reference/checkouts/create-checkout) takes the same `save_payment_method` and `customer.id`. If your customer pays by card, the card is saved the same way. Another way to pay saves nothing, so send `"channels": ["CARD"]` to take cards only.

### On its own

```http
POST /v1/payment-methods

{ "type": "CARD", "customer": { "id": "USER-123", "name": "Juan dela Cruz", "email": "juan@example.com" }, "success_url": "https://shop.example/account/cards" }
```

`customer.id` is your own ID for your customer. You get a payment method with `status: PENDING` and `next_action.checkout_url`. Send your customer there. On Weav's save page they:

1. Give their name, if you didn't send it.
2. Type their card and agree that you may charge it for future payments.
3. Confirm it with their bank. Their bank may show a ₱10.00 check that is canceled right after.

```mermaid
sequenceDiagram
    autonumber
    actor Customer as Your customer
    participant Server as Your server
    participant Weav
    participant Bank as Their bank
    Server->>Weav: POST /v1/payment-methods<br/>CARD, customer.id
    Weav-->>Server: 201 PENDING<br/>next_action.checkout_url
    Server-->>Customer: Sends them to checkout_url
    Customer->>Weav: Their name if you didn't send it,<br/>their card, and their agreement
    Weav->>Bank: Checks the card
    Bank-->>Customer: Asks them to confirm<br/>(may show a ₱10.00 check)
    Customer->>Bank: Confirms
    alt The bank confirms it
        Weav-->>Customer: Redirects to success_url
        Weav->>Server: PAYMENT_METHOD_SAVED webhook<br/>the card is ACTIVE
    else The check fails, or the link isn't used in 24 hours
        Weav->>Server: PAYMENT_METHOD_FAILED webhook
    end
```

Then they go back to your `success_url` (or `cancel_url`), and you get a webhook:

- `PAYMENT_METHOD_SAVED`: it's `ACTIVE`. Keep its `id` (`PM…`) with your customer. Show `label` ("Visa •••• 1381") so they know which card it is.
- `PAYMENT_METHOD_FAILED`: the bank's check failed, or the link wasn't used within 24 hours.

## 2. Charge it

```http
POST /v1/payments

{ "payment_method_id": "PM8K2Q7X9KBAT4MZ3N", "currency": "PHP", "amount": 499.00, "external_reference_id": "SUB-USER-123-2026-11" }
```

No `channel`, no `customer`, no URLs. A saved card is usually paid at once: the response says `COMPLETED`, and you get `PAYMENT_COMPLETED`. If the card can't be charged, it's `FAILED`. Sometimes the bank asks your customer to confirm again: the payment stays `PENDING` with `next_action.type` `REDIRECT`, so send your customer to `checkout_url`.

```mermaid
sequenceDiagram
    autonumber
    participant Server as Your server
    participant Weav
    actor Customer as Your customer
    Server->>Weav: POST /v1/payments<br/>payment_method_id, amount, external_reference_id
    alt Paid, as most charges are
        Weav-->>Server: 201 COMPLETED
        Weav->>Server: PAYMENT_COMPLETED webhook
    else The bank wants your customer to confirm
        Weav-->>Server: 201 PENDING<br/>next_action.checkout_url
        Server-->>Customer: Sends them the link
        Customer->>Weav: Confirms with their bank
        Weav->>Server: PAYMENT_COMPLETED webhook
    else The card can't be charged
        Weav-->>Server: 201 FAILED
        Weav->>Server: PAYMENT_FAILED webhook
    end
```

Retries are safe: send the same request with the same `external_reference_id` (see [Idempotency](https://docs.weavpayments.com/idempotency)).

## 3. List and remove

- `GET /v1/payment-methods?customer_id=USER-123`: your customer's saved cards.
- `GET /v1/payment-methods?expires_by=2026-12`: cards that expire by then. Each card has `exp_month` and `exp_year`, so you can ask for a new card in time.
- `GET /v1/payments?payment_method_id=PM8K2Q7X9KBAT4MZ3N`: every charge on a saved card.
- `DELETE /v1/payment-methods/{id}`: removes it, at the card processor too, so nobody can charge it again (`PAYMENT_METHOD_REMOVED`). Remove a card whenever your customer asks.

## Good to know

- **Your customer's agreement.** The save page records that your customer let you charge the card. Charge only what you told them you would (amounts and timing).
- **In a mobile app,** open the save page in the system browser or an in-app browser tab (`SFSafariViewController` on iOS, Custom Tabs on Android), not a WebView your app controls.
- **Test mode** has a test save page with three test cards: one that's paid, one that's declined, and one that expires this month. See [Test mode](https://docs.weavpayments.com/test-mode#test-saved-cards).
- **Subscriptions:** charge a saved card on your own schedule. See [Subscriptions](https://docs.weavpayments.com/subscriptions).
- Only cards for now (Visa, Mastercard, JCB).
