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

# Settle Payment

> Complete a pending payment with a settlement proof

<Info>
  After creating a payment via `/payment/create`, payment apps submit a signed proof from an accepted settlement provider to complete the payment.
</Info>

## Flow

1. Payment app creates pending payment via `/payment/create`
2. Payment app transfers funds to your OCID via an accepted settlement provider
3. Payment app receives signed proof from the settlement provider
4. Payment app submits proof to your `/payment/settle`
5. You verify proof, credit merchant, and return your own signed proof

## Implementation

```javascript theme={null}
app.post('/payment/settle', verifyAuth, async (req, res) => {
  const { txid, proof, signature } = req.body;
  const callerOcid = parseInt(req.headers['x-oc-id']);

  // 1. Find the pending payment
  const payment = await db.getPayment(txid);
  if (!payment) {
    return res.status(404).json({
      error: { code: 'PAYMENT_NOT_FOUND', message: 'Payment not found' }
    });
  }

  // 2. Check payment hasn't expired
  if (payment.expiresAt < Date.now() / 1000) {
    return res.status(400).json({
      error: { code: 'PAYMENT_EXPIRED', message: 'Payment has expired' }
    });
  }

  // 3. Check payment is still pending
  if (payment.status !== 'pending_settlement') {
    return res.status(400).json({
      error: { code: 'INVALID_STATE', message: `Payment is ${payment.status}` }
    });
  }

  // 4. Verify issuer is accepted
  if (!ACCEPTED_SETTLEMENT_PROVIDERS.includes(proof.issuer)) {
    return res.status(400).json({
      error: { code: 'ISSUER_NOT_ACCEPTED', message: 'Settlement provider not accepted' }
    });
  }

  // 5. Verify proof signature
  const publicKey = await getPublicKey(proof.issuer);
  const canonical = JSON.stringify(proof, Object.keys(proof).sort());
  if (!secp256k1_verify(publicKey, signature, canonical)) {
    return res.status(400).json({
      error: { code: 'PROOF_SIGNATURE_INVALID', message: 'Invalid proof signature' }
    });
  }

  // 6. Verify we are the recipient
  if (proof.to.ocid !== YOUR_OCID) {
    return res.status(400).json({
      error: { code: 'INVALID_PROOF', message: 'Proof recipient mismatch' }
    });
  }

  // 7. Verify amount matches
  if (proof.amount !== payment.amount || proof.currency !== payment.currency) {
    return res.status(400).json({
      error: { code: 'AMOUNT_MISMATCH', message: 'Amount does not match payment' }
    });
  }

  // 8. Complete payment
  await db.completePayment(payment.id, proof.txid, proof.timestamp);

  // 9. Credit merchant
  await db.creditMerchant(payment.merchantOcid, payment.amount, payment.currency);

  // 10. Create our proof for the merchant
  const merchantProof = {
    txid: payment.txid,
    issuer: YOUR_OCID,
    from: proof.from,
    to: { ocid: payment.merchantOcid, reference: payment.orderId },
    amount: payment.amount,
    currency: payment.currency,
    timestamp: Math.floor(Date.now() / 1000),
    memo: `Payment settled for ${payment.orderId}`
  };

  const merchantSignature = signProof(merchantProof);

  // 11. Notify merchant webhook
  await notifyMerchant(payment.merchantOcid, merchantProof, merchantSignature);

  // 12. Return our signed proof
  res.json({
    status: 'completed',
    txid: payment.txid,
    proof: merchantProof,
    signature: merchantSignature
  });
});
```

## Validation Checklist

Before completing a payment:

* [ ] Payment exists and matches the `txid`
* [ ] Payment hasn't expired
* [ ] Payment is in `pending_settlement` state
* [ ] Proof issuer is in your `accepts` list
* [ ] Proof signature is valid
* [ ] Proof recipient is your OCID
* [ ] Proof amount matches payment amount

## Response

On success, return your signed proof that the payment app can also use:

```json theme={null}
{
  "status": "completed",
  "txid": "gateway_payment_456",
  "proof": {
    "txid": "gateway_payment_456",
    "issuer": 300,
    "from": { "ocid": 200, "reference": "wallet_tx_123" },
    "to": { "ocid": 500, "reference": "ord_abc123" },
    "amount": "99.99",
    "currency": "USD",
    "timestamp": 1706500500
  },
  "signature": "gateway_sig_xyz..."
}
```


## OpenAPI

````yaml merchant-gateway-api/endpoint/merchant-gateway-api/openapi.json post /payment/settle
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:
  /payment/settle:
    post:
      summary: Settle payment
      description: >-
        Complete a pending payment by submitting a signed transfer proof from an
        accepted settlement provider. Verify the proof, mark the payment as
        complete, and forward a signed proof to the merchant's webhook.
      operationId: settlePayment
      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/PaymentSettleRequest'
            example:
              txid: gateway_payment_456
              proof:
                txid: settlement_tx_789
                issuer: 100
                from:
                  ocid: 200
                  reference: wallet_tx_123
                to:
                  ocid: 300
                  reference: gateway_payment_456
                amount: '99.99'
                currency: USD
                timestamp: 1706500500
                memo: Settlement for gateway_payment_456
              signature: a1b2c3d4e5f6...
      responses:
        '200':
          description: Payment settled successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentSettleResponse'
              example:
                status: completed
                txid: gateway_payment_456
                proof:
                  txid: gateway_payment_456
                  issuer: 300
                  from:
                    ocid: 200
                    reference: wallet_tx_123
                  to:
                    ocid: 500
                    reference: ord_abc123
                  amount: '99.99'
                  currency: USD
                  timestamp: 1706500500
                signature: gateway_sig_xyz...
        '400':
          description: Invalid proof or signature
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Payment not found
          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:
    PaymentSettleRequest:
      type: object
      required:
        - txid
        - proof
        - signature
      description: Settlement proof for a pending payment
      properties:
        txid:
          type: string
          description: Your payment transaction ID
        proof:
          $ref: '#/components/schemas/TransactionProof'
        signature:
          type: string
          description: Settlement provider's signature
    PaymentSettleResponse:
      type: object
      required:
        - status
        - txid
        - proof
        - signature
      description: Payment completed, your signed proof for the merchant
      properties:
        status:
          type: string
          enum:
            - completed
        txid:
          type: string
        proof:
          $ref: '#/components/schemas/TransactionProof'
        signature:
          type: string
          description: Your signature of the proof
    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
    TransactionProof:
      type: object
      required:
        - txid
        - issuer
        - from
        - to
        - amount
        - currency
        - timestamp
      properties:
        txid:
          type: string
        issuer:
          type: integer
        from:
          type: object
          required:
            - ocid
          properties:
            ocid:
              type: integer
            reference:
              type: string
        to:
          type: object
          required:
            - ocid
          properties:
            ocid:
              type: integer
            reference:
              type: string
        amount:
          type: string
        currency:
          type: string
        timestamp:
          type: integer
        memo:
          type: string

````