# Paytota card collection (simplified)

Local reference for collecting **card** payments via Paytota’s **Purchase** API: create a purchase, then either send the customer to **hosted checkout** or **POST card fields** to Paytota. For auth, webhooks (signatures), and account setup, see [paytota.md](paytota.md) and Paytota’s own documentation.

**Upstream:** [https://gate.paytota.com/](https://gate.paytota.com/login)

---

## Authentication and base URL

- **Base URL:** `https://gate.paytota.com` (append paths below).
- **Headers (required):**
  - `Authorization: Bearer {{secret key}}`
  - `Content-Type: application/json`

Retrieve the secret key and **Brand ID** from the merchant dashboard (**Developers** / account settings). Use **test** credentials and purchases when integrating; **live** and **test** data (including webhooks) do not cross over.

---

## Create purchase

**`POST /api/v1/purchases/`**

Creates a purchase and returns a **`checkout_url`** (and purchase **`id`**). You need at least client identity, line items, and `brand_id`.

**Example body (minimal):**

```json
{
  "client": {
    "email": "customer@example.com",
    "country": "UG",
    "city": "Kampala",
    "street_address": "Example Street",
    "zip_code": "12345",
    "state": "Central"
  },
  "purchase": {
    "currency": "USD",
    "products": [{ "name": "Order item", "price": 1000 }]
  },
  "skip_capture": false,
  "brand_id": "{{Brand Id}}",
  "reference": "your-internal-ref-001",
  "success_redirect": "https://yoursite.com/pay/success",
  "failure_redirect": "https://yoursite.com/pay/failure"
}
```

- **`success_redirect` / `failure_redirect`:** Required for redirect flows after payment; **required on the initial JSON** when using **direct post** (see below).
- **`skip_capture`:** As in Paytota docs; leave `false` unless your integration explicitly uses capture deferral.

**Response fields to use first:**

| Field            | Use |
| ---------------- | --- |
| `id`             | Purchase UUID; use for status polling and support. |
| `status`         | e.g. `created` until paid or failed. |
| `checkout_url`   | Hosted payment page **or** target URL for direct-post card form. |
| `reference`      | Echo of your reference when supplied. |
| `direct_post_url`| May be present depending on configuration; prefer `checkout_url` unless docs specify otherwise. |

Full responses include client, purchase totals, issuer details, redirects, etc. See Paytota’s official API reference for every field.

---

## Collecting card details (two methods)

### 1. Hosted checkout (redirect)

Redirect the customer’s browser to **`checkout_url`**. They enter card details on Paytota; after authorization, Paytota redirects them to **`success_redirect`** or **`failure_redirect`**.

### 2. Direct post

Submit card data as **`POST`** (typically `application/x-www-form-urlencoded` or `multipart/form-data` per your integration) to the same **`checkout_url`** returned from create purchase. The customer can be sent straight to the bank / 3DS step depending on the card.

**Form fields (typical):**

| Field             | Notes |
| ----------------- | ----- |
| `cardholder_name` | As on card. |
| `card_number`     | PAN (spaces often accepted). |
| `expires`         | e.g. `MM/YY`. |
| `cvc`             | Security code. |
| `remember_card`   | e.g. `true` / `false` if supported. |

**Important:** Include **`success_redirect`** and **`failure_redirect`** on the **initial** `POST /api/v1/purchases/` JSON when using direct post so Paytota can return the shopper to your site after processing.

**Example (HTML form posting to checkout):**

```html
<form action="{{checkout_url}}" method="POST">
  <input type="hidden" name="cardholder_name" value="Test User">
  <input type="hidden" name="card_number" value="4444333322221111">
  <input type="hidden" name="expires" value="12/30">
  <input type="hidden" name="cvc" value="123">
  <input type="hidden" name="remember_card" value="true">
  <noscript><input type="submit" name="continue" value="Continue"></noscript>
</form>
```

Use real user-supplied values in production; avoid logging full PAN or CVC.

---

## Test cards

Use a **test** purchase and test keys in the dashboard.

| PAN                 | Behaviour        |
| ------------------- | ---------------- |
| `4444 3333 2222 1111` | Non–3D Secure |
| `5555 5555 5555 4444` | 3D Secure     |

For both:

- Any **cardholder name**
- **Expiry** ≥ current month/year
- **CVC:** `123`

To simulate **failure**, change CVC or expiry to invalid values.

---

## Webhooks

Configure your webhook URL in the merchant **developer / webhook** settings. Paytota **POST**s a JSON body (purchase-shaped payload) when status changes.

- **`event_type`:** e.g. `purchase.paid`, `purchase.error` (exact set per Paytota docs).
- **`status`:** e.g. `paid` for success, `error` for failure.

**Minimal callback illustration:**

```json
{
  "id": "761196a9-6aa2-4afd-a59f-214ad6788e5e",
  "status": "paid",
  "event_type": "purchase.paid",
  "payment": {
    "amount": 2300,
    "currency": "USD",
    "paid_on": 1694606029
  },
  "transaction_data": {
    "flow": "direct_post",
    "extra": {
      "card_brand": "mastercard",
      "masked_pan": "537410******0294"
    }
  }
}
```

Verify deliveries using **`X-Signature`** and the webhook public key / **`GET .../public_key/`** flow described in [paytota.md](paytota.md).

---

## Check purchase status

**`GET /api/v1/purchases/{id}/`**

Same headers as create. Replace `{id}` with the purchase UUID from the create response. Use this to poll if needed; prefer webhooks for final state.

---

## Merchant admin / dashboard (“Admin views”)

Operators and integrators use the Paytota **merchant console** (log in at [gate.paytota.com](https://gate.paytota.com/login)):

- **API keys / secret:** Used as the Bearer token for `POST /api/v1/purchases/` and `GET /api/v1/purchases/{id}/`.
- **Brand ID:** Required on purchase create; copy from brand or developer settings.
- **Webhooks:** Define the callback URL and events in the **developer** section; test and live webhooks are separate (test webhooks do not fire for live purchases, and vice versa).
- **Purchases / transactions:** Search and open purchases by id or reference, inspect status history, refunds, and related metadata in the UI—useful alongside API polling and your own logs.

Screenshots for developer keys and webhook setup also appear at the top of [paytota.md](paytota.md).

---

## This application’s wrapper

Base path: **`{APP_BASE}/api/paytota/`** (see [app/Config/Routes.php](app/Config/Routes.php) and **`/api/docs`**).

| Method | Path | Purpose |
| ------ | ---- | ------- |
| POST | `/checkout` | General checkout; requires **phone** + email, country, currency, products, redirects. Returns `checkout_url`. |
| POST | **`/card/purchase`** | **Card collection** — same upstream purchase create with optional **phone** and extra **billing/shipping** client fields; returns `checkout_url` and `direct_post_url` when present. |
| GET | `/purchase/status/{id}` | Poll purchase; includes `transaction_data` and `status_history` when Paytota returns them (useful after card payment). |
| GET | **`/paytota/payment/success`** | Hosted **success** page after Paytota redirects the shopper (WebView / Flutter handoff). |
| GET | **`/paytota/payment/failure`** | Hosted **failure** page (same handoff behaviour). |

**Hosted card checkout:** Redirect the browser to `data.checkout_url` from either endpoint. **Card data is not sent to your backend.**

**Flutter / in-app WebView:** On **`POST /checkout`** or **`POST /card/purchase`**, you can send **`webview_return_url`** (a template such as `myapp://paytota?status={status}&reference={reference}&purchase_id={purchase_id}`) **instead of** `success_redirect` and `failure_redirect`. The API then points Paytota at the hosted pages above, which expand placeholders from Paytota’s redirect query, fire **`postMessage`** with a `paytota_result` payload, call **`flutter_inappwebview.callHandler('paytotaResult', …)`** when present, and offer **Return to app**. Optional **`webview_return_auto`** (default `true`) adds `auto_return=1` so the app URL opens shortly after load. You can still set explicit `success_redirect` / `failure_redirect` to these paths yourself and pass `return_url` + `auto_return` query params the same way; optional env **`paytota.webview_return_url`** supplies a default template when `return_url` is omitted on the result page.

**Direct post:** Build a form (or server-side POST) that targets **`checkout_url`** (or `direct_post_url` if your Paytota account returns one) with card fields — still **no PAN/CVC through this app’s API**. For raw **`POST https://gate.paytota.com/api/v1/purchases/`**, use your own server with the secret key or see Paytota docs.

---

## Further reading

For fields not covered here (tax lines, upsells, full error codes), use Paytota’s official documentation and the longer samples in [paytota.md](paytota.md).
