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

# Payment Examples

> Code examples for the C2B Payment API

# Payment Examples

Complete examples for initiating a payment. They use the `signRequest` / `sign_request` / `SignRequest` helper from the [Authentication](/getting-started/authentication#signing-helpers) page, which builds the v1 canonical string and returns the required auth headers.

## Initiate Payment

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    const axios = require('axios');
    const { signRequest } = require('./gopay-auth'); // see Authentication page

    const BASE_URL = 'https://api.gopay.example.com'; // your onboarding base URL

    async function initiatePayment() {
        const payload = {
            merchantId: process.env.GOPAY_MERCHANT_ID,
            amount: 100.5,
            currency: 'USD',
            phoneNumber: '+251912345678',
            successUrl: 'https://your-store.com/checkout/success',
            failUrl: 'https://your-store.com/checkout/failed',
            notifyUrl: 'https://your-store.com/api/gopay-webhook',
        };

        // Serialize ONCE — the same bytes must be hashed, signed and sent.
        const body = Buffer.from(JSON.stringify(payload), 'utf8');
        const url = `${BASE_URL}/api/v1/initiate-payment`;
        const headers = signRequest('POST', url, body);
        headers['Idempotency-Key'] = `order-${Date.now()}`; // safe retries

        try {
            const res = await axios.post(url, body, { headers });
            console.log('Session created:', res.data.sessionId);
            return res.data.checkoutUrl; // redirect the customer here
        } catch (err) {
            const e = err.response?.data?.error;
            console.error(`Payment failed [${e?.code}]: ${e?.message} (requestId=${e?.requestId})`);
            throw err;
        }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import json
    import requests

    from gopay_auth import sign_request  # see Authentication page

    BASE_URL = "https://api.gopay.example.com"  # your onboarding base URL


    def initiate_payment(merchant_id: str) -> str:
        payload = {
            "merchantId": merchant_id,
            "amount": 100.5,
            "currency": "USD",
            "phoneNumber": "+251912345678",
            "successUrl": "https://your-store.com/checkout/success",
            "failUrl": "https://your-store.com/checkout/failed",
            "notifyUrl": "https://your-store.com/api/gopay-webhook",
        }

        # Serialize ONCE — the same bytes must be hashed, signed and sent.
        body = json.dumps(payload).encode("utf-8")
        url = f"{BASE_URL}/api/v1/initiate-payment"
        headers = sign_request("POST", url, body)
        headers["Idempotency-Key"] = f"order-{payload['phoneNumber']}-001"

        resp = requests.post(url, data=body, headers=headers, timeout=15)
        if resp.status_code != 200:
            err = resp.json()["error"]
            raise RuntimeError(
                f"payment failed [{err['code']}]: {err['message']} "
                f"(requestId={err.get('requestId')})"
            )

        data = resp.json()
        print("Session created:", data["sessionId"])
        return data["checkoutUrl"]  # redirect the customer here
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "bytes"
        "encoding/json"
        "fmt"
        "io"
        "net/http"
        "os"
    )

    const baseURL = "https://api.gopay.example.com" // your onboarding base URL

    type initiatePaymentResponse struct {
        CheckoutURL string `json:"checkoutUrl"`
        SessionID   uint32 `json:"sessionId"`
    }

    type apiError struct {
        Error struct {
            Code      string `json:"code"`
            Message   string `json:"message"`
            RequestID string `json:"requestId"`
        } `json:"error"`
    }

    func initiatePayment(merchantID string) (string, error) {
        payload := map[string]any{
            "merchantId":  merchantID,
            "amount":      100.5,
            "currency":    "USD",
            "phoneNumber": "+251912345678",
            "successUrl":  "https://your-store.com/checkout/success",
            "failUrl":     "https://your-store.com/checkout/failed",
            "notifyUrl":   "https://your-store.com/api/gopay-webhook",
        }

        // Serialize ONCE — the same bytes must be hashed, signed and sent.
        body, err := json.Marshal(payload)
        if err != nil {
            return "", err
        }

        url := baseURL + "/api/v1/initiate-payment"
        // SignRequest is on the Authentication page
        headers, err := SignRequest(
            os.Getenv("GOPAY_PRIVATE_KEY"),
            os.Getenv("GOPAY_PUBLIC_KEY"),
            "POST", url, body,
        )
        if err != nil {
            return "", err
        }

        req, err := http.NewRequest("POST", url, bytes.NewReader(body))
        if err != nil {
            return "", err
        }
        for k, v := range headers {
            req.Header.Set(k, v)
        }
        req.Header.Set("Idempotency-Key", "order-0001") // safe retries

        resp, err := http.DefaultClient.Do(req)
        if err != nil {
            return "", err
        }
        defer resp.Body.Close()
        respBody, _ := io.ReadAll(resp.Body)

        if resp.StatusCode != http.StatusOK {
            var apiErr apiError
            _ = json.Unmarshal(respBody, &apiErr)
            return "", fmt.Errorf("payment failed [%s]: %s (requestId=%s)",
                apiErr.Error.Code, apiErr.Error.Message, apiErr.Error.RequestID)
        }

        var out initiatePaymentResponse
        if err := json.Unmarshal(respBody, &out); err != nil {
            return "", err
        }
        fmt.Println("Session created:", out.SessionID)
        return out.CheckoutURL, nil // redirect the customer here
    }
    ```
  </Tab>
</Tabs>

<Tip>
  Getting `INVALID_SIGNATURE`? Work through the [common mistakes](/getting-started/authentication#common-mistakes) on the Authentication page — the usual culprits are re-serialized bodies, millisecond timestamps, and signing with the wrong host.
</Tip>
