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

# Checkout Order

> Accept signed orders from merchants and return a redirect URL for hosted checkout

<Info>
  This is the core endpoint for hosted checkout. Merchants POST their signed orders, and you return a URL to your payment page.
</Info>

## Flow

1. Merchant creates and signs an order
2. Merchant POSTs order to your `/orders/checkout`
3. You verify the signature and create a checkout session
4. You return a `redirect_url` to your payment page
5. Merchant redirects their customer to your page
6. Customer completes payment on your hosted UI
7. You send signed proof to merchant's `/transfer/webhook`
8. You redirect customer back to merchant's `completed` URL

## Implementation

```javascript theme={null}
app.post('/orders/checkout', verifyAuth, async (req, res) => {
  const { order, signature, urls } = req.body;
  const merchantOcid = parseInt(req.headers['x-oc-id']);

  // 1. Verify merchant is registered
  const merchant = await db.getMerchant(merchantOcid);
  if (!merchant) {
    return res.status(401).json({
      error: { code: 'MERCHANT_NOT_REGISTERED', message: 'Unknown merchant' }
    });
  }

  // 2. Verify order signature
  const publicKey = merchant.publicKey;
  const canonical = JSON.stringify(order, Object.keys(order).sort());
  if (!secp256k1_verify(publicKey, signature, SHA256(canonical))) {
    return res.status(400).json({
      error: { code: 'INVALID_SIGNATURE', message: 'Order signature invalid' }
    });
  }

  // 3. Validate order
  if (order.expiresAt && order.expiresAt < Date.now() / 1000) {
    return res.status(400).json({
      error: { code: 'ORDER_EXPIRED', message: 'Order has expired' }
    });
  }

  // 4. Check we can settle with merchant
  if (!order.accepts.includes(YOUR_OCID)) {
    return res.status(400).json({
      error: { code: 'SETTLEMENT_NOT_SUPPORTED', message: 'Merchant does not accept this gateway' }
    });
  }

  // 5. Create checkout session
  const session = await db.createSession({
    id: generateSessionId(),
    merchantOcid,
    order,
    signature,
    urls,
    status: 'pending',
    createdAt: Date.now()
  });

  // 6. Return redirect URL
  res.json({
    redirect_url: `https://pay.yourgateway.com/checkout/${session.id}`
  });
});
```

## Validation Checklist

Before accepting an order:

* [ ] Merchant is registered with your gateway
* [ ] Order signature is valid (signed by merchant's private key)
* [ ] Order has not expired (`expiresAt`)
* [ ] Your OCID is in the `accepts` array
* [ ] Currency is supported
* [ ] Amount is reasonable (fraud checks)

## Hosted Payment Page

On your payment page (`/checkout/{sessionId}`):

1. Retrieve the session and order
2. Display order details (items, amount, merchant name)
3. Present payment options (cards, wallets, bank transfer)
4. Process payment using your internal systems
5. On success, call merchant's webhook and redirect to `urls.completed`
6. On failure/cancel, redirect to `urls.cancelled`

<Warning>
  Always verify the session hasn't expired and hasn't already been paid before processing payment.
</Warning>


## OpenAPI

````yaml merchant-gateway-api/endpoint/merchant-gateway-api/openapi.json post /orders/checkout
openapi: 3.1.0
info:
  title: Opencharge Merchant Gateway API
  description: >-
    API specification for merchant gateways integrating with the Opencharge
    protocol. Merchant gateways provide hosted checkout pages for online
    merchants and process payments on their behalf.
  version: 0.1.0
  license:
    name: MIT
servers:
  - url: https://pay.gateway.example/opencharge
    description: Example merchant gateway Opencharge endpoint
security: []
paths:
  /orders/checkout:
    post:
      summary: Checkout order
      description: >-
        Merchants call this endpoint to initiate a hosted checkout. Receive the
        merchant's signed order along with callback URLs. Create a checkout
        session and return a redirect URL where the merchant should send their
        customer to complete payment on your hosted page.
      operationId: checkoutOrder
      parameters:
        - $ref: '#/components/parameters/X-OC-ID'
        - $ref: '#/components/parameters/X-OC-Timestamp'
        - $ref: '#/components/parameters/X-OC-Nonce'
        - $ref: '#/components/parameters/X-OC-Signature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderCheckoutRequest'
            example:
              order:
                id: ord_xyz789
                ocid: 500
                reference: checkout-session-123
                amount: '99.99'
                currency: USD
                items:
                  - id: prod_001
                    name: Premium Widget
                    quantity: 2
                    price: '49.99'
                memo: Online purchase
                createdAt: 1706313600
                expiresAt: 1706400000
                accepts:
                  - 100
                  - 101
              signature: a1b2c3d4e5f6...merchant_sig
              urls:
                order:
                  - https://merchant.example/orders/ord_xyz789
                completed: https://merchant.example/checkout/success
                cancelled: https://merchant.example/checkout/cancelled
      responses:
        '200':
          description: Checkout session created, redirect URL returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderCheckoutResponse'
              example:
                redirect_url: https://pay.gateway.example/checkout/session_abc123
        '400':
          description: Invalid request or signature
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  parameters:
    X-OC-ID:
      name: X-OC-ID
      in: header
      required: true
      description: Caller's OCID (Opencharge ID)
      schema:
        type: string
      example: '500'
    X-OC-Timestamp:
      name: X-OC-Timestamp
      in: header
      required: true
      description: Unix timestamp in seconds
      schema:
        type: string
      example: '1706500000'
    X-OC-Nonce:
      name: X-OC-Nonce
      in: header
      required: true
      description: Unique request identifier for replay protection
      schema:
        type: string
      example: req_abc123
    X-OC-Signature:
      name: X-OC-Signature
      in: header
      required: true
      description: secp256k1 ECDSA signature of the canonical request
      schema:
        type: string
      example: a1b2c3d4...7f1b
  schemas:
    OrderCheckoutRequest:
      type: object
      required:
        - order
        - signature
        - urls
      description: Request from merchant to initiate hosted checkout
      properties:
        order:
          $ref: '#/components/schemas/Order'
        signature:
          type: string
          description: Merchant's signature of the order
        urls:
          type: object
          required:
            - completed
            - cancelled
          properties:
            order:
              type: array
              items:
                type: string
                format: uri
            completed:
              type: string
              format: uri
            cancelled:
              type: string
              format: uri
    OrderCheckoutResponse:
      type: object
      required:
        - redirect_url
      description: Response with redirect URL for hosted checkout
      properties:
        redirect_url:
          type: string
          format: uri
          description: URL to redirect customer to complete payment
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - INVALID_SIGNATURE
                - TIMESTAMP_EXPIRED
                - NONCE_REUSED
                - UNKNOWN_OCID
                - INVALID_PROOF
                - PROOF_SIGNATURE_INVALID
                - ISSUER_NOT_ACCEPTED
                - ORDER_NOT_FOUND
                - ORDER_EXPIRED
                - TXID_NOT_FOUND
                - MERCHANT_NOT_REGISTERED
                - INSUFFICIENT_FUNDS
                - PAYMENT_NOT_FOUND
                - PAYMENT_EXPIRED
                - ACCOUNT_NOT_FOUND
            message:
              type: string
            details:
              type: object
    Order:
      type: object
      required:
        - id
        - ocid
        - amount
        - currency
        - createdAt
      description: Signed order object from merchant
      properties:
        id:
          type: string
        ocid:
          type: integer
          description: Merchant's OCID
        reference:
          type: string
        amount:
          type: string
        currency:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'
        memo:
          type: string
        expiresAt:
          type: integer
        createdAt:
          type: integer
        accepts:
          type: array
          items:
            type: integer
          description: Settlement providers the merchant accepts
    OrderItem:
      type: object
      required:
        - id
        - name
        - quantity
        - price
      properties:
        id:
          type: string
        name:
          type: string
        quantity:
          type: number
        price:
          type: string

````