Skip to content

Checkout from your site

GIEP's Shopify storefront script (/storefront/v1.js, see the Shopify runbook) reads Shopify's /cart.js and lets GIEP price the cart from Shopify's own API. Both depend on Shopify, so it does not work on other platforms, and there is no WooCommerce variant of the script.

On any other site, your server creates the checkout session and sends the shopper to the URL it returns. The price comes from your own backend (your catalog, your cart), which is the same trust model as the Shopify path: the browser never decides an amount.

Text
shopper clicks "Pay"  ->  your server prices the cart  ->  POST /v1/checkout-sessions  ->  redirect to { url }

Server snippet (Node 22, TypeScript)

TypeScript
// POST /checkout on YOUR server. The API key lives in your secrets manager, never in the browser.
import { createHash } from 'node:crypto';

export async function startCheckout(cart: { id: string; lines: Array<{ sku: string; title: string; qty: number }> }) {
  // Prices come from YOUR catalog, in integer minor units (4999 = 49.99 USD). Never from the request.
  const lineItems = cart.lines.map((l) => ({
    title: l.title,
    quantity: l.qty,
    unitAmount: priceInMinorUnits(l.sku), // your function
  }));
  // Deterministic key: a retry or a double click returns the same session (persist it with the cart).
  const key = 'cart-' + cart.id + '-' + createHash('sha256').update(JSON.stringify(lineItems)).digest('hex').slice(0, 16);
  const res = await fetch(process.env.GIEP_API_URL + '/v1/checkout-sessions', {
    method: 'POST',
    headers: {
      authorization: 'Bearer ' + process.env.GIEP_API_KEY,
      'content-type': 'application/json',
      'idempotency-key': key,
    },
    body: JSON.stringify({
      checkoutId: process.env.GIEP_CHECKOUT_ID, // a published checkout of the store
      currency: 'USD',
      lineItems,
      externalOrderRef: cart.id,
      successUrl: 'https://shop.example/thanks', // must be in the store's return-URL allow-list
    }),
    signal: AbortSignal.timeout(10_000),
  });
  if (!res.ok) throw new Error('GIEP refused the session: ' + res.status); // show your own checkout instead
  const { url } = (await res.json()) as { url: string };
  return url; // 302 the browser here
}

Server snippet (PHP)

PHP
<?php
// POST /checkout on YOUR server. The API key lives in your server configuration, never in the browser.
function giep_start_checkout(array $cart): string
{
    // Prices come from YOUR catalog, in integer minor units (4999 = 49.99 USD). Never from the request.
    $lineItems = [];
    foreach ($cart['lines'] as $l) {
        $lineItems[] = [
            'title' => $l['title'],
            'quantity' => (int) $l['qty'],
            'unitAmount' => price_in_minor_units($l['sku']), // your function
        ];
    }
    // Deterministic key: a retry or a double click returns the same session.
    $key = 'cart-' . $cart['id'] . '-' . substr(hash('sha256', json_encode($lineItems)), 0, 16);
    $ch = curl_init(getenv('GIEP_API_URL') . '/v1/checkout-sessions');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => [
            'authorization: Bearer ' . getenv('GIEP_API_KEY'),
            'content-type: application/json',
            'idempotency-key: ' . $key,
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'checkoutId' => getenv('GIEP_CHECKOUT_ID'), // a published checkout of the store
            'currency' => 'USD',
            'lineItems' => $lineItems,
            'externalOrderRef' => (string) $cart['id'],
            'successUrl' => 'https://shop.example/thanks', // must be in the store's return-URL allow-list
        ]),
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    if ($body === false || $status >= 300) {
        throw new RuntimeException('GIEP refused the session: ' . $status); // show your own checkout instead
    }
    return json_decode($body, true)['url']; // redirect the browser here
}

Server snippet (Python)

Python
import hashlib
import json
import os

import requests


def start_checkout(cart: dict) -> str:
    # Prices come from YOUR catalog, in integer minor units (4999 = 49.99 USD). Never from the request.
    line_items = [
        {"title": l["title"], "quantity": l["qty"], "unitAmount": price_in_minor_units(l["sku"])}  # your function
        for l in cart["lines"]
    ]
    # Deterministic key: a retry or a double click returns the same session.
    key = "cart-%s-%s" % (cart["id"], hashlib.sha256(json.dumps(line_items).encode()).hexdigest()[:16])
    res = requests.post(
        os.environ["GIEP_API_URL"] + "/v1/checkout-sessions",
        headers={
            "authorization": "Bearer " + os.environ["GIEP_API_KEY"],
            "idempotency-key": key,
        },
        json={
            "checkoutId": os.environ["GIEP_CHECKOUT_ID"],  # a published checkout of the store
            "currency": "USD",
            "lineItems": line_items,
            "externalOrderRef": str(cart["id"]),
            "successUrl": "https://shop.example/thanks",  # must be in the store's return-URL allow-list
        },
        timeout=10,
    )
    res.raise_for_status()  # show your own checkout instead of failing the sale
    return res.json()["url"]  # redirect the browser here

Rules that matter:

  • A timeout is not a failure: retry with the same idempotency key (a replay returns the same session with a fresh token), never with a new one.
  • Mark the order paid only from GIEP's signed order.paid webhook or GET /v1/payments/{id}, never from the shopper coming back to successUrl.
  • If GIEP does not answer, fall back to your own checkout rather than failing the sale.
  • For WooCommerce use the GIEP Checkout plugin (see the merchant guide), which does exactly this from the order.

Reference: Checkout sessions and payment links.