> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paynext.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Charge Returning Customers

> Charge customers who already have a payment method on file—SDK checkout or API-only.

Charge customers who have already completed a payment with PayNext. Their payment method is securely stored, so you can process additional charges without collecting card details again.

## When You Need This

* Sell add-ons, credits, or one-time purchases to existing customers
* Upgrade or downgrade subscription plans
* Recover failed subscription payments
* Process charges without showing checkout UI

## Decision Flowchart

<div className="block dark:hidden">
  ```mermaid theme={"system"}
  flowchart LR
      A([Charge returning<br/>customer]) --> B{Customer<br/>present?}
      
      subgraph sdk ["SDK Checkout"]
          C([One-time payment])
          D([Change subscription<br/>plan])
          E([Recover past-due])
      end
      
      subgraph api ["API-Only"]
          F([One-time payment /<br/>new subscription])
          K([Change subscription<br/>plan])
      end
      
      subgraph session ["Create Client Session"]
          G(["customer.id + plan.id"])
          H(["customer.id + plan.id<br/>+ subscription.id<br/>+ proration_billing_mode"])
          I(["customer.id<br/>+ subscription.id"])
      end
      
      subgraph payments ["POST /payments"]
          J(["customer_id + plan_id"])
          L(["customer_id + plan_id<br/>+ subscription_id<br/>+ proration_billing_mode"])
      end

      B -->|Yes| C
      B -->|Yes| D
      B -->|Yes| E
      B -->|No| F
      B -->|No| K
      C --> G
      D --> H
      E --> I
      F --> J
      K --> L

      style A fill:#f1f5f9,stroke:#64748b,color:#334155
      style B fill:#fef3c7,stroke:#d97706,color:#92400e
      style C fill:#dbeafe,stroke:#60a5fa,color:#1e40af
      style D fill:#dbeafe,stroke:#60a5fa,color:#1e40af
      style E fill:#dbeafe,stroke:#60a5fa,color:#1e40af
      style F fill:#f3e8ff,stroke:#c084fc,color:#7c3aed
      style K fill:#f3e8ff,stroke:#c084fc,color:#7c3aed
      style G fill:#f1f5f9,stroke:#94a3b8,color:#334155
      style H fill:#f1f5f9,stroke:#94a3b8,color:#334155
      style I fill:#f1f5f9,stroke:#94a3b8,color:#334155
      style J fill:#f1f5f9,stroke:#94a3b8,color:#334155
      style L fill:#f1f5f9,stroke:#94a3b8,color:#334155
      style sdk fill:#eff6ff,stroke:#bfdbfe,color:#1f2937
      style api fill:#faf5ff,stroke:#e9d5ff,color:#1f2937
      style session fill:#f8fafc,stroke:#e2e8f0,color:#1f2937
      style payments fill:#f8fafc,stroke:#e2e8f0,color:#1f2937
  ```
</div>

<div className="hidden dark:block">
  ```mermaid theme={"system"}
  flowchart LR
      A([Charge returning<br/>customer]) --> B{Customer<br/>present?}
      
      subgraph sdk ["SDK Checkout"]
          C([One-time payment])
          D([Change subscription<br/>plan])
          E([Recover past-due])
      end
      
      subgraph api ["API-Only"]
          F([One-time payment /<br/>new subscription])
          K([Change subscription<br/>plan])
      end
      
      subgraph session ["Create Client Session"]
          G(["customer.id + plan.id"])
          H(["customer.id + plan.id<br/>+ subscription.id<br/>+ proration_billing_mode"])
          I(["customer.id<br/>+ subscription.id"])
      end
      
      subgraph payments ["POST /payments"]
          J(["customer_id + plan_id"])
          L(["customer_id + plan_id<br/>+ subscription_id<br/>+ proration_billing_mode"])
      end

      B -->|Yes| C
      B -->|Yes| D
      B -->|Yes| E
      B -->|No| F
      B -->|No| K
      C --> G
      D --> H
      E --> I
      F --> J
      K --> L

      style A fill:#1e293b,stroke:#475569,color:#cbd5e1
      style B fill:#78350f,stroke:#d97706,color:#fef3c7
      style C fill:#1e3a5f,stroke:#60a5fa,color:#bfdbfe
      style D fill:#1e3a5f,stroke:#60a5fa,color:#bfdbfe
      style E fill:#1e3a5f,stroke:#60a5fa,color:#bfdbfe
      style F fill:#3b1d5c,stroke:#c084fc,color:#e9d5ff
      style K fill:#3b1d5c,stroke:#c084fc,color:#e9d5ff
      style G fill:#1e293b,stroke:#475569,color:#cbd5e1
      style H fill:#1e293b,stroke:#475569,color:#cbd5e1
      style I fill:#1e293b,stroke:#475569,color:#cbd5e1
      style J fill:#1e293b,stroke:#475569,color:#cbd5e1
      style L fill:#1e293b,stroke:#475569,color:#cbd5e1
      style sdk fill:#172554,stroke:#1e3a8a,color:#e5e7eb
      style api fill:#2e1065,stroke:#4c1d95,color:#e5e7eb
      style session fill:#1e293b,stroke:#334155,color:#e5e7eb
      style payments fill:#1e293b,stroke:#334155,color:#e5e7eb
  ```
</div>

<Warning>
  **Key difference:** SDK Checkout automatically prompts the customer to update their card if payment fails. API-Only charges require you to notify the customer separately.
</Warning>

## Choose Your Approach

| I want to...                           | Approach                              | Best For                                        |
| -------------------------------------- | ------------------------------------- | ----------------------------------------------- |
| Show checkout and let customer confirm | [SDK Checkout](#sdk-checkout)         | Most scenarios—automatic card update on failure |
| Charge silently without UI             | [API-Only Charges](#api-only-charges) | Silent plan changes, automated billing          |

<Tip>
  **Use SDK checkout when possible.** If payment fails, the customer can immediately update their card—significantly improving success rates.
</Tip>

## SDK Checkout

Display checkout with the customer's saved payment method. If payment fails, they can switch to a new card instantly.

### Client Session Parameters

| Use Case                     | Parameters                                                               | Result                   |
| ---------------------------- | ------------------------------------------------------------------------ | ------------------------ |
| **One-time payment**         | `customer.id` + `plan.id`                                                | Payment only             |
| **Change subscription plan** | `customer.id` + `plan.id` + `subscription.id` + `proration_billing_mode` | Plan updated + payment   |
| **Recover past-due**         | `customer.id` + `subscription.id`                                        | Subscription reactivated |

<Note>
  For plan changes, include both the **new** `plan.id` and the **existing** `subscription.id`. For recovery, omit `plan.id`—PayNext uses the subscription's current plan.
</Note>

<Tip>
  **One-off plans** accept a custom price: add `plan.price` (`{ "amount", "currency" }`) to the client session to override the plan's amount for this checkout. See [Custom Pricing](/guides/platform/plans#custom-pricing).
</Tip>

### Example: One-Time Charge

```bash cURL theme={"system"}
curl -X POST https://api.paynext.com/client-session \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-API-Version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": { "id": "cus_11dfa45f-23b1-40f4-9e9b-c9d485915528" },
    "plan": { "id": "plan_7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d" },
    "options": { "payment_methods_mode": "saved_or_new" }
  }'
```

Then mount the SDK with the returned session `id`:

```javascript theme={"system"}
const checkout = new PayNextCheckout()

checkout.mount('checkout-container', {
  clientToken: session.id,
  environment: 'sandbox',
  onCheckoutComplete: (result) => {
    console.log('Payment successful:', result.payment_id)
  },
  onCheckoutFail: (error) => {
    // Customer can retry with a different card in the SDK
    console.error('Payment failed:', error.status_reason?.message)
  }
})
```

### Payment Method Display

| Mode                     | Behavior                                |
| ------------------------ | --------------------------------------- |
| `saved_or_new` (default) | Show saved card, allow switching to new |
| `new_only`               | Collect new card only                   |

<Tabs>
  <Tab title="saved_or_new">
    Shows the customer's saved card with an option to add a new one.

    <Frame>
      <div style={{ background: 'linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%)', padding: '60px 40px', borderRadius: '8px', textAlign: 'center' }}>
        <p style={{ color: '#0369a1', fontWeight: 500, margin: 0 }}>Demo: saved\_or\_new checkout flow</p>
        <p style={{ color: '#64748b', fontSize: '14px', marginTop: '8px' }}>Animated demo coming soon</p>
      </div>
    </Frame>
  </Tab>

  <Tab title="new_only">
    Ignores saved payment methods and collects a new card.

    <Frame>
      <div style={{ background: 'linear-gradient(135deg, #fefce8 0%, #fef3c7 100%)', padding: '60px 40px', borderRadius: '8px', textAlign: 'center' }}>
        <p style={{ color: '#a16207', fontWeight: 500, margin: 0 }}>Demo: new\_only checkout flow</p>
        <p style={{ color: '#64748b', fontSize: '14px', marginTop: '8px' }}>Animated demo coming soon</p>
      </div>
    </Frame>
  </Tab>
</Tabs>

## API-Only Charges

Charge a customer's saved payment method without displaying checkout UI. PayNext processes synchronously and returns the result immediately.

<Warning>
  **Not recommended for most use cases.** If payment fails, you must notify the customer and get them back to your UI to update their card. SDK checkout handles this automatically.
</Warning>

### When to Use

| Scenario                             | Example                                             |
| ------------------------------------ | --------------------------------------------------- |
| One-time payment or new subscription | Add-ons, credits, or start subscription without UI  |
| Change subscription plan             | Silent plan upgrade/downgrade with immediate charge |

### Create a Charge

```bash cURL theme={"system"}
curl -X POST https://api.paynext.com/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-API-Version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cus_11dfa45f-23b1-40f4-9e9b-c9d485915528",
    "plan_id": "plan_7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"
  }'
```

<Expandable title="Request parameters">
  | Parameter                | Required | Description                                                                                                                        |
  | ------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
  | `customer_id`            | Yes      | PayNext customer ID                                                                                                                |
  | `plan_id`                | Yes      | Plan ID that defines amount and currency                                                                                           |
  | `price`                  | No       | Custom price `{ "amount", "currency" }` overriding the plan's price. **One-off plans only.** See [Custom Pricing](#custom-pricing) |
  | `subscription_id`        | No       | Include for plan changes                                                                                                           |
  | `proration_billing_mode` | No       | Required with `subscription_id` for plan changes                                                                                   |
</Expandable>

#### Custom Pricing

For one-off plans, override the plan's amount for this charge by adding a `price` object—useful for variable-amount purchases without a dedicated plan:

```bash cURL theme={"system"}
curl -X POST https://api.paynext.com/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-API-Version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cus_11dfa45f-23b1-40f4-9e9b-c9d485915528",
    "plan_id": "plan_7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d",
    "price": { "amount": 4999, "currency": "USD" }
  }'
```

<Note>
  `amount` is in [minor units](/guides/payments/currencies). The custom price overrides both the default and any localized pricing. Passing `price` with a recurring plan returns a `400` error.
</Note>

**Response:**

```json theme={"system"}
{
  "id": "pay_e8a1b2c3-d4f5-6789-abcd-ef0123456789",
  "payment_status": "SETTLED",
  "amount": 1999,
  "currency_code": "USD",
  "payment_type": "MIT",
  "customer": {
    "id": "cus_11dfa45f-23b1-40f4-9e9b-c9d485915528"
  },
  "payment_method": {
    "id": "pm_d3b07384-d9a5-4d16-a5b1-3fa3d9b0b123",
    "type": "CARD",
    "details": {
      "last4": "4242",
      "brand": "visa"
    }
  }
}
```

### Handle Failures

When a charge fails, check the `advice_code` to determine next steps. See [Decline Codes](/guides/payments/decline-codes) for the full list.

<Note>
  **For plan changes without immediate payment**, use the [Schedule Plan Change](/guides/use-cases/change-subscription-plan#schedule-plan-change) API instead—no charge occurs until the next billing date.
</Note>

## Subscription Renewals

PayNext automatically handles subscription renewals—you don't need to create charges manually. Use the approaches above only for:

* One-time purchases (add-ons, credits)
* Silent plan upgrades/downgrades
* Manual recovery after failed renewals

<Note>
  For failed subscription renewals, PayNext moves the subscription to `past_due`. Use [Recover Subscriptions](/guides/use-cases/recover-subscriptions) to collect payment with customer interaction.
</Note>
