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

# Submit Order to Session

> Receive orders when merchants scan your app user's QR code

<Info>
  This endpoint receives orders when a merchant scans your user's QR code. The `sessionId` identifies which user will pay.
</Info>

## Flow

1. User opens your app and requests to pay
2. You generate a `sessionId` and display a QR code
3. Merchant scans the QR code
4. Merchant POSTs their signed order to this endpoint
5. You validate the order and prompt user for confirmation
6. User confirms, you process payment
7. You send proof to merchant's webhook

## Implementation

## Session Management

When your mobile app user requests to display a qrcode for the merchant to scan
generate secure, single-use sessions and generate a qrCode.

```javascript theme={null}
// This is your internal app endpoint and it is not part of api the spec.
// Lets assume you mobile app calls this endpoint to get teh QrCode.

app.post('/app/pay', authenticateUser, async (req, res) => {
  const user = req.user;

  // Generate session
  const sessionId = crypto.randomBytes(16).toString('hex');

  await db.createSession({
    id: sessionId,
    userId: user.id,
    status: 'pending',
    expiresAt: Date.now() + 5 * 60 * 1000 // 5 minutes
  });

  // Build QR payload -> url points to endpoint /orders/create/:sessionId below
  const qrPayload = {
    ocid: YOUR_OCID,
    order: `https://api.yourwallet.com/opencharge/orders/create/${sessionId}`,
    expiresAt: Math.floor((Date.now() + 5 * 60 * 1000) / 1000)
  };

  res.json({
    sessionId,
    qrPayload,
    qrCode: await generateQRCode(JSON.stringify(qrPayload))
  });
});
```

The merchant app scans the qrCode, and posts the order and signature to the session order url you set in the qrcode.

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

  // 1. Find session
  const session = await db.getSession(sessionId);
  if (!session) {
    return res.status(404).json({
      error: { code: 'SESSION_NOT_FOUND', message: 'Session not found' }
    });
  }

  // 2. Check expiration
  if (session.expiresAt < Date.now()) {
    return res.status(404).json({
      error: { code: 'SESSION_EXPIRED', message: 'Session has expired' }
    });
  }

  // 3. Check not already used
  if (session.status !== 'pending') {
    return res.status(410).json({
      error: { code: 'SESSION_USED', message: 'Session already used' }
    });
  }

  // 4. Verify merchant signature on order
  const merchantPublicKey = await getMerchantPublicKey(merchantOcid);
  if (!verifyOrderSignature(order, signature, merchantPublicKey)) {
    return res.status(400).json({
      error: { code: 'INVALID_SIGNATURE', message: 'Invalid order signature' }
    });
  }

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

  // 6. Check we can settle
  if (!canSettle(order.accepts)) {
    return res.status(400).json({
      error: { code: 'SETTLEMENT_NOT_SUPPORTED', message: 'Cannot settle with merchant' }
    });
  }

  // 7. Invalidate session and store order
  await db.updateSession(sessionId, {
    status: 'order_received',
    order,
    signature,
    merchantOcid,
    urls
  });

  // 8. If your user needs to confirm orders, Notify your user's app to load the order. (websocket/push)
  await notifyUser(session.userId, 'payment_request', {
    merchantOcid,
    order,
    sessionId
  });

  // 9. Return status URL
  res.json({
    urls: {
      status: `https://api.yourwallet.com/orders/${order.id}/status`
    }
  });

});
```

## Processing Payment After User Confirms

If your user needs to confirm orders, implement an internal endpoint to process confirmations.

```javascript theme={null}
app.post('/app/confirm/:sessionId', authenticateUser, async (req, res) => {
  const { sessionId } = req.params;
  const user = req.user;

  const session = await db.getSession(sessionId);

  // Verify session belongs to user
  if (session.userId !== user.id) {
    return res.status(403).json({ error: 'Not your session' });
  }

  // Check user has sufficient balance
  if (user.balance < parseFloat(session.order.amount)) {
    return res.status(402).json({ error: 'Insufficient funds' });
  }

  // Process payment
  await processPayment(session, user);

  res.json({ success: true });
});
```

## Settlement

After collecting payment from the user, you must settle with the merchant through a trusted gateway. See the [Settlement Guide](/payment-gateway-api/settlement) for the complete details.

```javascript theme={null}
async function processPayment(session, user) {
  // Debit user
  await db.debitUser(user.id, session.order.amount);

  // Settle with merchant - see Settlement Guide for strategies
  const proof = await settlePayment(user, session.order, session.merchantOcid);

  // Update session
  await db.updateSession(session.id, { status: 'completed', proof });
}
```

<Card title="Settlement Guide" icon="arrow-right" href="/payment-gateway-api/settlement">
  Learn about direct settlement, partner reserves, and third-party settlement
</Card>


## OpenAPI

````yaml payment-gateway-api/endpoint/payment-gateway-api/openapi.json post /orders/create/{sessionId}
openapi: 3.1.0
info:
  title: Opencharge Payment Gateway API
  description: >-
    API specification for payment gateways integrating with the Opencharge
    protocol. Payment gateways help end users pay merchants through wallet apps
    or hosted checkout pages.
  version: 0.1.0
  license:
    name: MIT
servers:
  - url: https://api.wallet.example/opencharge
    description: Example payment gateway Opencharge endpoint
security: []
paths:
  /orders/create/{sessionId}:
    post:
      summary: Submit order to session
      description: >-
        Merchants POST signed orders to this endpoint after scanning your user's
        QR code. The sessionId identifies which user will pay. Verify the order,
        debit the user, and return a status URL.
      operationId: createOrderSession
      parameters:
        - name: sessionId
          in: path
          required: true
          description: Temporary session ID from QR code that identifies the paying user
          schema:
            type: string
        - $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/OrderSessionRequest'
            example:
              order:
                id: ord_abc123
                ocid: 500
                reference: POS-001
                amount: '25.00'
                currency: USD
                items:
                  - id: item_001
                    name: Coffee
                    quantity: 2
                    price: '5.00'
                  - id: item_002
                    name: Sandwich
                    quantity: 1
                    price: '15.00'
                memo: 'Order #42'
                createdAt: 1706313600
                expiresAt: 1706400000
                accepts:
                  - 100
                  - 101
                  - 102
              signature: a1b2c3d4e5f6...merchant_sig
              urls:
                order:
                  - https://merchant.example/orders/ord_abc123
                completed: https://merchant.example/checkout/success
                cancelled: https://merchant.example/checkout/cancelled
      responses:
        '200':
          description: Order received, payment processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderSessionResponse'
              example:
                urls:
                  status: https://api.wallet.example/orders/ord_abc123/status
        '400':
          description: Invalid request or signature
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Session not found or expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '410':
          description: Session already used
          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
      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
      schema:
        type: string
      example: req_abc123
    X-OC-Signature:
      name: X-OC-Signature
      in: header
      required: true
      description: secp256k1 ECDSA signature
      schema:
        type: string
      example: a1b2c3d4...7f1b
  schemas:
    OrderSessionRequest:
      type: object
      required:
        - order
        - signature
        - urls
      properties:
        order:
          $ref: '#/components/schemas/Order'
        signature:
          type: string
        urls:
          type: object
          properties:
            order:
              type: array
              items:
                type: string
                format: uri
            completed:
              type: string
              format: uri
            cancelled:
              type: string
              format: uri
    OrderSessionResponse:
      type: object
      required:
        - urls
      properties:
        urls:
          type: object
          properties:
            status:
              type: string
              format: uri
    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
                - SESSION_NOT_FOUND
                - SESSION_EXPIRED
                - SESSION_USED
                - INVALID_PROOF
                - PROOF_SIGNATURE_INVALID
                - ISSUER_NOT_ACCEPTED
                - INSUFFICIENT_FUNDS
                - ACCOUNT_NOT_FOUND
            message:
              type: string
            details:
              type: object
    Order:
      type: object
      required:
        - id
        - ocid
        - amount
        - currency
        - createdAt
      properties:
        id:
          type: string
        ocid:
          type: integer
        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
    OrderItem:
      type: object
      required:
        - id
        - name
        - quantity
        - price
      properties:
        id:
          type: string
        name:
          type: string
        quantity:
          type: number
        price:
          type: string

````