Weav Docsv1

Guides / Payments

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.

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.

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

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

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.
  • Subscriptions: charge a saved card on your own schedule. See Subscriptions.
  • Only cards for now (Visa, Mastercard, JCB).