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

# Booking Lifecycle: Hold Now, Charge Later

> Place a hold at booking, keep one token, and charge the balance, extras and refunds later through each operator's own Payment Gateway Account.

<Info>
  This page is part of the **REST API Guides**. Card capture happens in the frontend through the Payments Library; see the [Payments Library Guides](/guides/library/setup).
</Info>

**Prerequisites:** [API key](/getting-started/generate-api-key), a [Payment Gateway Account](/getting-started/add-payment-provider) for each operator, and the [Payments Library](/guides/library/setup) in your booking page.

This guide is for rental, tour, venue and other booking platforms. Each operator on your platform has its own payment gateway, and a booking produces several payments over time: a deposit, a balance, extras, and sometimes a refund.

## The Pattern

1. **At booking**, place a hold on the card and save a token for it in one step.
2. **Later**, capture the hold, charge the balance, charge for extras or damage, and refund part of a payment. Every call uses the same token.
3. **Every payment** goes through the operator's own Payment Gateway Account.

The token is an Orchestra token. It is not tied to one payment gateway, so the same card can be charged through whichever Payment Gateway Account you name on each call. A token only works in the Orchestra account that created it.

| Stage | Call | Guide |
| - | - | - |
| Booking: hold and token | Payments Library session with `PREAUTH_AND_TOKENIZE` | [Payments Library Setup](/guides/library/setup) |
| Collect the deposit | `PUT /PaymentGateway/capture` | [Authorize & Capture](/guides/rest-api/authorize-capture) |
| Balance on a set date | `POST /PaymentGateway/charge`, or a payment contract | [Charge Payments](/guides/rest-api/charge), [Scheduled Payments](/guides/rest-api/scheduled-payments) |
| Extras or damage | `POST /PaymentGateway/charge` | [Charge Payments](/guides/rest-api/charge) |
| Partial refund | `PUT /PaymentGateway/refund` | [Refunds & Voids](/guides/rest-api/refunds-voids) |
| Release an unused hold | `DELETE /PaymentGateway/void` | [Refunds & Voids](/guides/rest-api/refunds-voids) |

## 1. At Booking: Hold and Tokenize

Start a Payments Library session with the `PREAUTH_AND_TOKENIZE` operation. Set `amount` to the amount you want to hold and `paymentGatewayAccountId` to the operator's Payment Gateway Account.

```bash theme={null}
curl -X POST https://api.orchestrasolutions.com/EWalletOperations \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -d '{
    "operation": "PREAUTH_AND_TOKENIZE",
    "paymentGatewayAccountId": "operator1042Stripe",
    "amount": 150.00,
    "currencyCode": "EUR",
    "mode": "LIVE",
    "customerEmail": "guest@example.com",
    "merchantReference": "booking-7731"
  }'
```

The customer enters their card in your page. Orchestra places the hold through the operator's Payment Gateway Account. If the hold succeeds, Orchestra also saves the card and returns a token. If 3D Secure is set up for the session, it runs at this point. See [Library 3D Secure](/guides/library/3d-secure).

When the session completes, send the results token to `validateResults` from your server (see [Result Handling](/guides/library/result-handling)). The response contains the parts you need to keep:

```json theme={null}
{
  "upgChargeResults": {
    "operationResultCode": "Success",
    "operationType": "PreAuth",
    "gatewayName": "Stripe",
    "gatewayReference": "pi_3Q8xY2",
    "amount": 150.00,
    "currency": "EUR"
  },
  "tokenAndMaskedCardModel": {
    "token": "YOUR_TOKEN",
    "bankCard": {
      "number": "************1234",
      "expirationMonth": 12,
      "expirationYear": 2027,
      "nameOnCard": "Jane Smith"
    }
  }
}
```

Store these values with the booking:

| Value | Where it comes from | Used for |
| - | - | - |
| Token | `tokenAndMaskedCardModel.token` | Every later call, as `@` plus the token in `card.cardNumber` |
| Cardholder name and expiry | `tokenAndMaskedCardModel.bankCard` | Every later call. The token replaces the card number only. |
| Hold reference | `upgChargeResults.gatewayReference` | `refTransId` when you capture or void the hold |
| Payment Gateway Account | The account you set in the session | `paymentGatewayAccountName` when you capture or void the hold |

<Warning>
  If the session lists fallback gateways and the operator's gateway declines, the hold may be placed by a fallback gateway. `upgChargeResults` always holds the final attempt, and its `gatewayName` shows which gateway placed the hold. Capture or void the hold through that gateway's Payment Gateway Account.
</Warning>

## 2. Collect the Deposit: Capture the Hold

When you are ready to take the deposit, capture the hold. Send the capture to the Payment Gateway Account that placed the hold, with the hold reference in `refTransId`:

```json theme={null}
{
  "refTransId": "pi_3Q8xY2",
  "amount": 150.00,
  "currency": "EUR",
  "paymentGatewayAccountName": "operator1042Stripe",
  "card": {
    "cardNumber": "@YOUR_TOKEN",
    "cardHolderName": "Jane Smith",
    "expirationMonth": 12,
    "expirationYear": 2027
  }
}
```

Send this body with `PUT /PaymentGateway/capture`. See [Capture](/guides/rest-api/authorize-capture#capture) for full examples in each language.

<Note>
  A hold does not last forever. How long it stays valid depends on your payment processor and the card, so check with your payment processor and capture before it expires.
</Note>

## 3. Charge the Balance on a Set Date

You have two options. Both use the token you stored at booking.

<Tabs>
  <Tab title="Your own charge">
    Your system calls `POST /PaymentGateway/charge` on the due date, with the balance amount and the operator's Payment Gateway Account:

    ```json theme={null}
    {
      "amount": 450.00,
      "currency": "EUR",
      "paymentGatewayAccountName": "operator1042Stripe",
      "card": {
        "cardNumber": "@YOUR_TOKEN",
        "cardHolderName": "Jane Smith",
        "expirationMonth": 12,
        "expirationYear": 2027
      }
    }
    ```

    The result comes back on the request, including the transaction reference you need for a later refund. You can also add fallback gateways. See [Charge Payments](/guides/rest-api/charge).
  </Tab>

  <Tab title="A payment contract">
    Create a payment contract that charges once. Set `startDate` to the due date and `maxOccurrences` to `1`:

    ```json theme={null}
    {
      "templateId": "YOUR_TEMPLATE_ID",
      "name": "Booking 7731 balance",
      "tokenId": "YOUR_TOKEN",
      "startDate": "2026-11-15",
      "maxOccurrences": 1,
      "chargeRequestData": {
        "amount": 450.00,
        "currency": "EUR",
        "myRef": "booking-7731-balance",
        "cardHolderName": "Jane Smith",
        "expirationMonth": 12,
        "expirationYear": 2027
      }
    }
    ```

    Send this body with `POST /PaymentContract`. The contract needs a payment schedule template, which you create in the Orchestra portal. A template charges through one Payment Gateway Account, so create one template for each operator's account. See [Scheduled Payments](/guides/rest-api/scheduled-payments).

    Check the outcome with `GET /PaymentContract/{id}`. After a successful charge, `currentOccurrences` is `1` and `status` is `Completed`.
  </Tab>
</Tabs>

<Warning>
  A payment contract charges only through the template's Payment Gateway Account. It does not use fallback gateways. It sends the charge without a security code (CVV) and without 3D Secure data. If the charge fails, the contract stays `Active` and Orchestra tries again on the template's schedule until a charge succeeds. If you collect the balance another way, cancel the contract.
</Warning>

<Tip>
  Choose your own charge if you need the transaction reference straight away, for example to refund part of the balance later. The contract API reports whether the charge succeeded, but it does not return the transaction reference.
</Tip>

## 4. Charge for Extras or Damage

For extras, incidentals or damage found after the stay, charge the stored token with `POST /PaymentGateway/charge`. The request body is the same as in [Your own charge](#3-charge-the-balance-on-a-set-date) above, with the new amount. The security code (`cvv`) is optional for a tokenized card.

## 5. Refund Part of a Payment

To return part of a captured deposit or a charge, send `PUT /PaymentGateway/refund` with a smaller `amount`. Use the Payment Gateway Account that processed the payment and the transaction reference it returned:

```json theme={null}
{
  "refTransId": "original-transaction-id",
  "amount": 50.00,
  "currency": "EUR",
  "paymentGatewayAccountName": "operator1042Stripe",
  "card": {
    "cardNumber": "@YOUR_TOKEN",
    "cardHolderName": "Jane Smith",
    "expirationMonth": 12,
    "expirationYear": 2027
  }
}
```

See [Partial Refunds](/guides/rest-api/refunds-voids#partial-refunds).

## 6. Release a Hold You Will Not Use

If the booking is cancelled before you capture, void the hold with `DELETE /PaymentGateway/void`. Send the same hold reference, amount and Payment Gateway Account as for the capture. See [Void](/guides/rest-api/refunds-voids#void).

<Warning>
  Do not wait for a hold to expire. If you are not going to capture it, void it so the customer's funds are released.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Authorize & Capture" icon="hand-holding-dollar" href="/guides/rest-api/authorize-capture">
    Capture and void in detail
  </Card>

  <Card title="Scheduled Payments" icon="calendar" href="/guides/rest-api/scheduled-payments">
    Templates and payment contracts
  </Card>

  <Card title="Refunds & Voids" icon="rotate-left" href="/guides/rest-api/refunds-voids">
    Return funds or release a hold
  </Card>

  <Card title="Result Handling" icon="check" href="/guides/library/result-handling">
    Validate the session result on your server
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.