// Taking money with Square: a card payment, one authorized and captured later, a refund, a
// customer found by email, an item in the catalog, lists past one page, and a webhook checked.
//
//   SQUARE_ACCESS_TOKEN=... SQUARE_API_URL=https://connect.squareupsandbox.com quillo run square.qwe
//   then open /pay, /later, /refund, /customers, /catalog, /lists, /hook-check and /failures
//
// In the sandbox, `cnon:card-nonce-ok` is a good card and `cnon:card-nonce-declined` a declined
// one. Point a webhook subscription at POST /square/webhook, and set SQUARE_WEBHOOK_KEY and
// SQUARE_WEBHOOK_URL to its signature key and exact URL.

use "square.qwer"

route GET /pay {
    let paid = square_pay("cnon:card-nonce-ok", square_money("19.99", "USD")?, { reference_id: "order-1001", note: "Blue mug" })?
    let read = square_payment(paid.id)?
    { state: read.status, amount: read.amount_money.amount, reference: read.reference_id }
}

// The same key sent twice is one payment; a changed amount with that key is refused.
route GET /again {
    let key = square_new_key()
    let one = square_pay("cnon:card-nonce-ok", square_money("5.00", "USD")?, { idempotency_key: key })?
    let two = square_pay("cnon:card-nonce-ok", square_money("5.00", "USD")?, { idempotency_key: key })?
    {
        same: one.id == two.id,
        changed: square_pay("cnon:card-nonce-ok", square_money("6.00", "USD")?, { idempotency_key: key }).error,
        long: square_pay("cnon:card-nonce-ok", square_money("6.00", "USD")?, { idempotency_key: repeat("k", 46) }).error,
    }
}

route GET /later {
    let held = square_pay("cnon:card-nonce-ok", square_money("40", "USD")?, { autocomplete: false })?
    let done = square_payment_complete(held.id)?
    let other = square_pay("cnon:card-nonce-ok", square_money("7", "USD")?, { autocomplete: false })?
    { held: held.status, done: done.status, cancelled: square_payment_cancel(other.id)?.status, twice: square_payment_complete(other.id).error }
}

route GET /refund {
    let paid = square_pay("cnon:card-nonce-ok", square_money("30", "USD")?, {})?
    let back = square_refund(paid.id, square_money("10", "USD")?, "Chipped mug", "")?
    {
        state: back.status,
        settled: square_refund_get(back.id)?.status,
        too_much: square_refund(paid.id, square_money("99", "USD")?, "", "").error,
    }
}

route GET /customers {
    let ada = square_customer_create({ given_name: "Ada", family_name: "Lovelace", email_address: "ada@example.com" })?
    square_customer_update(ada.id, { note: "Prefers mail", version: ada.version })?
    {
        name: square_customer(ada.id)?.given_name,
        note: square_customer(ada.id)?.note,
        found: map(square_customer_by_email("ada@example.com")?, (c) => c.family_name),
        stale: square_customer_update(ada.id, { note: "x", version: 0 }).error,
        bad: square_customer_create({ email_address: "not an address" }).error,
    }
}

route GET /catalog {
    let made = square_catalog_item("Blue mug", [{ name: "Small", price: "12.50", currency: "USD" }, { name: "Large", price: "1500", currency: "JPY" }])?
    let item = made.object
    {
        name: item.item_data.name,
        prices: map(item.item_data.variations, (v) => v.item_variation_data.price_money.amount),
        temp_ids: map(made.id_mappings, (m) => m.client_object_id),
        deleted: len(square_catalog_delete(item.id)?),
        none: square_catalog_item("x", []).error,
    }
}

route GET /lists = {
    payments: len(square_payments({ location_id: "L1" }, 0)?),
    capped: len(square_payments({ location_id: "L1" }, 130)?),
    customers: len(square_customers(0)?),
    items: len(square_catalog_list("ITEM", 0)?),
    categories: len(square_catalog_list("CATEGORY", 0)?),
}

route GET /money = {
    dollars: square_money("19.99", "usd")?,
    yen: square_money("1500", "JPY")?,
    dinars: square_money("1.250", "KWD")?,
    too_fine: square_money("10.5", "JPY").error,
    not_money: square_money("ten", "USD").error,
    code: square_money("1", "US").error,
}

route POST /square/webhook {
    let event = square_webhook(env("SQUARE_WEBHOOK_URL", ""), request.body, request.headers["x-square-hmacsha256-signature"] ?? "", env("SQUARE_WEBHOOK_KEY", ""))
    if is_err(event) { reply 403 event.error }
    { got: event.type }
}

// A signature made outside this plugin, by node's crypto.
route GET /hook-check {
    let url = "https://example.com/square/webhook"
    let body = "{\"merchant_id\":\"6SSW7HV8K2ST5\",\"type\":\"payment.updated\",\"event_id\":\"e1\"}"
    let sig = "wIOiyGj8OCbrrClH8Rvf4GlTu6SITiDf5SqDHCIAbYk="
    {
        good: square_webhook(url, body, sig, "whk-test-key")?.type,
        tampered: square_webhook(url, body + " ", sig, "whk-test-key").error,
        other_url: square_webhook("https://example.com/other", body, sig, "whk-test-key").error,
        unsigned: square_webhook(url, body, "", "whk-test-key").error,
    }
}

route GET /failures = {
    declined: square_pay("cnon:card-nonce-declined", square_money("1", "USD")?, {}).error,
    missing: square_payment("nope").error,
    waited: square_pay("cnon:rate-limit-once", square_money("2", "USD")?, {})?.status,
}
