Integrations

# Shopify Integration

Zazu doesn't yet ship a native Shopify Payments app. In the meantime, several merchants run Zazu alongside Shopify using the patterns below.

## What works today

You can accept card payments for Shopify orders, with one constraint: **the buyer leaves the Shopify checkout page** to pay on a Zazu-hosted page. Once payment completes, you reconcile the Shopify order with a webhook.

- [Checkout Sessions API](https://zazu.africa/docs/api/checkout-sessions) — server-created, one-off, expire in 30 minutes by default. Best fit for cart checkout.
- [Payment Links API](https://zazu.africa/docs/api/payment-links) — single-use or reusable URLs you email or SMS to a buyer. Best fit for manual invoicing.
- [Webhooks](https://zazu.africa/docs/webhooks) — checkout_session.completed and payment_link.paid events for reconciliation.

> **Note:** In-checkout payment, Shopify-admin refunds, and “Buy with Zazu” theme buttons require a Shopify Payments app, which we don't yet provide. If your business depends on any of these, get in touch.

## Which pattern fits you?

- **Pattern A — Post-purchase redirect. **In-house engineering; cleanest buyer experience without a Payments App. A custom Shopify app subscribes to orders/create, calls POST /api/checkout_sessions server-side, then sends the buyer the returned url (email/SMS, or a Plus post-purchase checkout extension).
- **Pattern B — Workflow automation (no-code). **Trigger on Shopify's orders/create in n8n/Zapier/Make, create a checkout session, email/SMS the url, mark the order paid on checkout_session.completed. Simpler to build; higher abandonment than A.
- **Pattern C — Standalone Payment Links. **Direct sales (Instagram/WhatsApp/in-person) with Shopify as catalog only. Create a payment link per product and send it through your usual channels.

## Example: create a checkout session from your Shopify app

`POST` `/api/checkout_sessions`

The `metadata.shopify_order_id` is echoed back in the checkout_session.completed webhook — use it to look up the Shopify order and mark it paid via the Shopify Admin API. completed fires the moment the buyer's payment is confirmed (the session then reads clearing until the provider settles); wait for checkout_session.settled instead only if your flow must not run before funds have actually landed.

cURL

CLI

JavaScript

Ruby

Python

Go

Rust

Elixir

Crystal

PHP

```console
curl -X POST "https://zazu.africa/api/checkout_sessions" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "01964a3b-0000-7000-8000-ac6000000a01",
    "amount": 1500.00,
    "success_url": "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://your-shop.myshopify.com/cart",
    "metadata": { "shopify_order_id": "5142318973029" },
    "description": "Order #1042"
  }'
```

```console
zazu checkout-sessions create \
  --account-id 01964a3b-0000-7000-8000-ac6000000a01 \
  --amount 1500.00 \
  --success-url "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}" \
  --cancel-url "https://your-shop.myshopify.com/cart"
```

```javascript
import { Zazu } from "@getzazu/sdk";
const zazu = new Zazu({ apiKey: process.env.ZAZU_API_KEY });

await zazu.checkoutSessions.create({
  account_id: "01964a3b-0000-7000-8000-ac6000000a01",
  amount: "1500.00",
  success_url: "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
  cancel_url: "https://your-shop.myshopify.com/cart",
  metadata: { shopify_order_id: "5142318973029" },
});
```

```ruby
require "zazu"
zazu = Zazu.new(api_key: ENV["ZAZU_API_KEY"])

zazu.checkout_sessions.create(
  account_id: "01964a3b-0000-7000-8000-ac6000000a01",
  amount: "1500.00",
  success_url: "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
  cancel_url: "https://your-shop.myshopify.com/cart",
  metadata: { shopify_order_id: "5142318973029" }
)
```

```python
from zazu_sdk import Zazu
client = Zazu(api_key="sk_live_...")

client.checkout_sessions.create(
    account_id="01964a3b-0000-7000-8000-ac6000000a01",
    amount="1500.00",
    success_url="https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
    cancel_url="https://your-shop.myshopify.com/cart",
    metadata={"shopify_order_id": "5142318973029"},
)
```

```go
import zazu "github.com/getzazu/zazu-go"

client, _ := zazu.New(zazu.WithAPIKey(os.Getenv("ZAZU_API_KEY")))

session, err := client.CheckoutSessions.Create(ctx, zazu.Attributes{
    "account_id":  "01964a3b-0000-7000-8000-ac6000000a01",
    "amount":      "1500.00",
    "success_url": "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
    "cancel_url":  "https://your-shop.myshopify.com/cart",
    "metadata":    map[string]any{"shopify_order_id": "5142318973029"},
})
```

```rust
let client = zazu_sdk::Client::builder()
    .api_key(std::env::var("ZAZU_API_KEY")?)
    .build()?;

let session = client.checkout_sessions().create(&serde_json::json!({
    "account_id": "01964a3b-0000-7000-8000-ac6000000a01",
    "amount": "1500.00",
    "success_url": "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://your-shop.myshopify.com/cart",
    "metadata": { "shopify_order_id": "5142318973029" },
}))?;
```

```elixir
{:ok, client} = Zazu.new(api_key: System.fetch_env!("ZAZU_API_KEY"))

{:ok, session} =
  Zazu.CheckoutSessions.create(client, %{
    "account_id" => "01964a3b-0000-7000-8000-ac6000000a01",
    "amount" => "1500.00",
    "success_url" => "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
    "cancel_url" => "https://your-shop.myshopify.com/cart",
    "metadata" => %{"shopify_order_id" => "5142318973029"}
  })
```

```crystal
require "zazu"

client = Zazu::Client.new # reads ZAZU_API_KEY

session = client.checkout_sessions.create(
  account_id: "01964a3b-0000-7000-8000-ac6000000a01",
  amount: "1500.00",
  success_url: "https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}",
  cancel_url: "https://your-shop.myshopify.com/cart",
  metadata: {shopify_order_id: "5142318973029"}
)
```

```php
use Zazu\Client;

$client = new Client(apiKey: getenv('ZAZU_API_KEY'));

$session = $client->checkoutSessions->create([
    'account_id' => '01964a3b-0000-7000-8000-ac6000000a01',
    'amount' => '1500.00',
    'success_url' => 'https://your-shop.myshopify.com/account/orders?session={CHECKOUT_SESSION_ID}',
    'cancel_url' => 'https://your-shop.myshopify.com/cart',
    'metadata' => ['shopify_order_id' => '5142318973029'],
]);
```

## Reconciliation

> **Warning:** The canonical signal that a buyer paid is the webhook, not the browser redirect. Buyers can close the tab, lose network, or refresh.

1. Verify the webhook signature using the X-Zazu-Signature header.
2. Look up the Shopify order via metadata.shopify_order_id (A/B) or payment_reference (C).
3. Mark the order paid via the Shopify Admin API (orderMarkAsPaid mutation). checkout_session.completed is the payment confirmation — the session may still read clearing (settled_at null) until the provider settles and checkout_session.settled fires.
4. Return 2xx within 10 seconds; Zazu retries non-2xx on the standard schedule.

Refunds: Zazu settles card payments directly into your bank account, so refunds aren't issued by Zazu or visible from the Shopify admin — you issue them from your bank.