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

# Create Transfer

> Process transfer requests to pay merchants

<Info>
  Use this endpoint for both internal transfers (your app paying a merchant) and external partner transfers.
</Info>

## Use Cases

1. **Internal** - Your mobile app requests payment after user scans merchant QR
2. **External** - Partner gateways with reserve accounts request transfers

## Implementation

```javascript theme={null}
app.post('/transfer/create', verifyAuth, async (req, res) => {
  const { from, to, amount, currency, memo, order } = req.body;
  const callerOcid = parseInt(req.headers['x-oc-id']);

  // Determine if internal or external
  if (isInternalRequest(callerOcid, from)) {
    return handleInternalTransfer(req, res);
  } else {
    return handleExternalTransfer(req, res);
  }
});

async function handleInternalTransfer(req, res) {
  const { from, to, amount, currency, memo, order } = req.body;

  // Get user from reference
  const user = await db.getUserByReference(from.reference);
  if (!user) {
    return res.status(400).json({
      error: { code: 'ACCOUNT_NOT_FOUND', message: 'User not found' }
    });
  }

  // Check balance
  if (user.balance < parseFloat(amount)) {
    return res.status(402).json({
      error: { code: 'INSUFFICIENT_FUNDS', message: 'Not enough balance' }
    });
  }

  // Process transfer
  const txid = generateTxid();
  await db.debitUser(user.id, amount);

  // Create proof
  const proof = {
    txid,
    issuer: YOUR_OCID,
    from: { ocid: YOUR_OCID, reference: from.reference },
    to: { ocid: to.ocid, reference: to.reference || order?.id },
    amount,
    currency,
    timestamp: Math.floor(Date.now() / 1000),
    memo
  };

  const signature = signProof(proof);

  // Notify merchant
  await sendMerchantWebhook(to.ocid, proof, signature);

  res.json({ proof, signature });
}
```

## Paying After Scanning Merchant QR

When your user scans a merchant's QR code:

```javascript theme={null}
async function payMerchantOrder(user, merchantOrder) {
  // 1. Verify the order
  const merchant = await fetchMerchantMetadata(merchantOrder.ocid);
  if (!verifyOrderSignature(merchantOrder.order, merchantOrder.signature, merchant.config.publicKey)) {
    throw new Error('Invalid order signature');
  }

  // 2. Debit user
  await db.debitUser(user.id, merchantOrder.order.amount);

  // 3. Settle with merchant - see Settlement Guide
  const proof = await settlePayment(user, merchantOrder.order, merchantOrder.ocid);

  return proof;
}
```

<Card title="Settlement Guide" icon="arrow-right" href="/payment-gateway-api/settlement">
  Learn about the different ways to settle payments with merchants
</Card>


## OpenAPI

````yaml payment-gateway-api/endpoint/payment-gateway-api/openapi.json post /transfer/create
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:
  /transfer/create:
    post:
      summary: Create transfer
      description: >-
        Process transfer requests to move funds between accounts. Used for
        internal transfers (from your own payment app) and external transfers
        (from 3rd party partners with a reserve account). Map the from/to OCIDs
        to internal merchant accounts. Return a signed proof of transfer.
      operationId: createTransfer
      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/TransferCreateRequest'
            example:
              from:
                ocid: 200
                reference: wallet_user_123
              to:
                ocid: 500
                reference: ord_abc123
              amount: '25.00'
              currency: USD
              memo: Payment for order ord_abc123
              order:
                id: ord_abc123
                urls:
                  - https://merchant.example/orders/ord_abc123
      responses:
        '200':
          description: Transfer completed, signed proof returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransferCreateResponse'
              example:
                proof:
                  txid: gateway_tx_789
                  issuer: 300
                  from:
                    ocid: 200
                    reference: wallet_user_123
                  to:
                    ocid: 500
                    reference: ord_abc123
                  amount: '25.00'
                  currency: USD
                  timestamp: 1706500500
                  memo: Payment for order ord_abc123
                signature: a1b2c3d4e5f6...
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          description: Insufficient funds
          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:
    TransferCreateRequest:
      type: object
      required:
        - from
        - to
        - amount
        - currency
      description: Request to transfer funds between accounts
      properties:
        from:
          type: object
          required:
            - ocid
          properties:
            ocid:
              type: integer
              description: Sender's OCID
            reference:
              type: string
              description: Sender's internal reference
        to:
          type: object
          required:
            - ocid
          properties:
            ocid:
              type: integer
              description: Recipient's OCID (merchant)
            reference:
              type: string
              description: Order ID or reference
        amount:
          type: string
          description: Amount as decimal string
        currency:
          type: string
          description: ISO 4217 currency code
        memo:
          type: string
        order:
          type: object
          properties:
            id:
              type: string
            urls:
              type: array
              items:
                type: string
                format: uri
    TransferCreateResponse:
      type: object
      required:
        - proof
        - signature
      description: Signed proof of completed transfer
      properties:
        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

````