ZERO × EZKART PROOF OF CONCEPT

One checkout.
Shipping to settlement.

ZERO sends signed products and weights. Ezkart finds the delivery price, collects the full customer payment, creates the Biteship shipment, tracks it, and prints the label.

Server signedPrice and weight cannot be edited in the redirect.
Customer pays shippingThe merchant platform fee never enters the customer total.
Webhook authoritativeA browser redirect can never mark an order paid.
CLICK EACH STEP

What calls what—and the JSON it uses

The Ezkart routes are ours. Provider routes show the exact Biteship and Duitku endpoint names used by this build.

01Official ZERO → EzkartCreate Checkout SessionPOST⌄
Ezkart/api/v1/checkout-sessions

Headers: X-Ezkart-Merchant, X-Ezkart-Timestamp, X-Ezkart-Signature, Idempotency-Key.

REQUEST
{
    "merchant_order_reference": "ZERO-2026-00042",
    "currency": "IDR",
    "items": [
        {
            "sku": "ZERO-SYRUP-500",
            "name": "ZERO Syrup 500 ml",
            "quantity": 2,
            "unit_price": 89000,
            "unit_weight_grams": 650
        }
    ],
    "declared_totals": {
        "merchandise": 178000,
        "weight_grams": 1300
    },
    "customer": {
        "name": "",
        "email": "",
        "phone": ""
    },
    "return_urls": {
        "success": "https://zerofoods.id/order/success",
        "cancel": "https://zerofoods.id/cart"
    }
}
RESPONSE
{
    "session_id": "cs_opaque_random_id",
    "status": "OPEN",
    "checkout_url": "https://checkout.example.id/c/cs_opaque_random_id",
    "expires_at": "2026-07-31T15:30:00+07:00"
}
02Ezkart → BiteshipSearch AreaGET⌄
Biteship/v1/maps/areas?countries=ID&type=single&input=Jakarta+Selatan

Called only after the customer types at least three location characters.

RESPONSE
{
    "success": true,
    "areas": [
        {
            "id": "IDNP6IDNC148IDND843IDZ12250",
            "name": "Pesanggrahan, Jakarta Selatan, DKI Jakarta. 12250",
            "postal_code": 12250
        }
    ]
}
03Ezkart → BiteshipRetrieve Courier RatesPOST⌄
Biteship/v1/rates/couriers

Ezkart signs the selected quote for 15 minutes and calculates Rp196,000 total.

REQUEST
{
    "origin_area_id": "ORIGIN_AREA_ID",
    "destination_area_id": "IDNP6IDNC148IDND843IDZ12250",
    "couriers": "jne,sicepat,anteraja,jnt,tiki",
    "items": [
        {
            "name": "ZERO Syrup 500 ml",
            "category": "food_and_drink",
            "sku": "ZERO-SYRUP-500",
            "value": 89000,
            "quantity": 2,
            "weight": 650
        }
    ]
}
RESPONSE
{
    "success": true,
    "pricing": [
        {
            "courier_name": "JNE",
            "courier_code": "jne",
            "courier_service_name": "Reguler",
            "courier_service_code": "reg",
            "duration": "1 - 2 days",
            "price": 18000
        }
    ]
}
04Ezkart → DuitkuCreate InvoicePOST⌄
Duitkuhttps://api-sandbox.duitku.com/api/merchant/createInvoice

The itemDetails prices add up exactly to paymentAmount. Website/platform fee is not charged here.

REQUEST
{
    "paymentAmount": 196000,
    "merchantOrderId": "cs_opaque_random_id",
    "productDetails": "Ezkart order ZERO-2026-00042",
    "customerVaName": "Test Customer",
    "email": "customer@example.com",
    "phoneNumber": "081234567890",
    "itemDetails": [
        {
            "name": "ZERO Syrup 500 ml",
            "price": 178000,
            "quantity": 2
        },
        {
            "name": "Shipping - JNE Reguler",
            "price": 18000,
            "quantity": 1
        }
    ],
    "callbackUrl": "https://checkout.example.id/api/v1/webhooks/duitku",
    "returnUrl": "https://checkout.example.id/payment/return?session=cs_opaque_random_id",
    "expiryPeriod": 15
}
RESPONSE
{
    "merchantCode": "DXXXX",
    "reference": "DXXXXS875LXXXX32IJZ7",
    "paymentUrl": "https://app-sandbox.duitku.com/redirect_checkout?reference=…",
    "statusCode": "00",
    "statusMessage": "SUCCESS"
}
05Duitku → EzkartPayment CallbackPOST FORM⌄
Duitku webhook/api/v1/webhooks/duitku

This signed server callback—not returnUrl—changes the order to PAID.

REQUEST
{
    "merchantCode": "DXXXX",
    "amount": "196000",
    "merchantOrderId": "cs_opaque_random_id",
    "paymentCode": "VA",
    "resultCode": "00",
    "reference": "DXXXXCX80TXXX5Q70QCI",
    "publisherOrderId": "provider-order-id",
    "signature": "HMAC_SHA256(merchantCode + amount + merchantOrderId)"
}
RESPONSE
{
    "ok": true
}
06Ezkart → BiteshipCreate an OrderPOST⌄
Biteship/v1/orders

Created only after a verified paid callback. Sandbox orders are simulated.

REQUEST
{
    "shipper_organization": "ZERO Foods via Ezkart",
    "origin_contact_name": "ZERO Fulfillment",
    "origin_contact_phone": "0812…",
    "origin_address": "Configured warehouse address",
    "origin_area_id": "ORIGIN_AREA_ID",
    "origin_collection_method": "pickup",
    "destination_contact_name": "Test Customer",
    "destination_contact_phone": "081234567890",
    "destination_address": "Customer street address",
    "destination_area_id": "IDNP6IDNC148IDND843IDZ12250",
    "courier_company": "jne",
    "courier_type": "reg",
    "delivery_type": "now",
    "reference_id": "cs_opaque_random_id",
    "items": [
        {
            "name": "ZERO Syrup 500 ml",
            "value": 89000,
            "quantity": 2,
            "weight": 650
        }
    ]
}
RESPONSE
{
    "success": true,
    "id": "5dd599ebdefcd4158eb8470b",
    "courier": {
        "tracking_id": "tracking-id",
        "waybill_id": "WYB-1112223333443",
        "company": "jne",
        "type": "reg",
        "routing_code": "CGK-01"
    },
    "price": 18000,
    "status": "confirmed"
}
07Biteship → EzkartOrder WebhooksPOST⌄
Biteship webhook/api/v1/webhooks/biteship

Installed for order.status, order.price, and order.waybill_id with a custom secret header.

REQUEST
{
    "event": "order.status",
    "order_id": "5dd599ebdefcd4158eb8470b",
    "status": "dropping_off",
    "courier_tracking_id": "tracking-id",
    "courier_waybill_id": "WYB-1112223333443"
}
RESPONSE
{
    "received": true,
    "event": "order.status",
    "order_id": "5dd599ebdefcd4158eb8470b"
}
08Ezkart operationsRender A5 Shipping LabelGET⌄
Ezkart/api/v1/orders/{session_id}/label

No Biteship label endpoint exists. Ezkart renders its own label from Create Order + webhook data.

RESPONSE
{
    "content_type": "text/html",
    "paper": "A5 portrait",
    "barcode": "Code 128-B"
}
A5 SHIPPING LABEL

Constructed by Ezkart from Biteship order data.

Biteship does not expose a Shipping Label API. This service combines the response from POST /v1/orders and webhook updates into a Code 128 label containing the courier, routing code, tracking number, sender, recipient, weight, and instructions.

JNE REG
EZKART-TEST-001
TOTest Customer

Jakarta Selatan, 12250
1.20 kg • 1 parcel