@vonpay/checkout-cli 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +110 -7
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -53,7 +53,7 @@ All commands are namespaced under `vonpay checkout`:
53
53
  | `vonpay checkout refunds list` | List one payment's refunds and its refund totals (`--payment-intent` or `--transaction`) |
54
54
  | `vonpay checkout tokens create` | Create a payment-method token for saved-card / MIT flows |
55
55
  | `vonpay checkout capabilities` | Show what this merchant's binder supports |
56
- | `vonpay checkout trigger <event>` | Trigger a synthetic webhook event against your local endpoint (`session.succeeded`, `session.failed`, `payment_intent.*`, `charge.refunded`) |
56
+ | `vonpay checkout trigger <event>` | Trigger a synthetic webhook event against your local endpoint (`charge.*`, `refund.failed`, `payment_intent.*`, `mirror.order.created`, `subscription.*`, `merchant.card_testing_suspected`, `session.*`; `vonpay checkout trigger --help` lists them all) |
57
57
  | `vonpay checkout listen` | Stream live webhook delivery attempts; optionally re-forward each to a local URL with `--forward-to` |
58
58
  | `vonpay checkout health` | Check API health and latency |
59
59
  | `vonpay checkout doctor` | Diagnose runtime + env + live API probe |
package/dist/index.js CHANGED
@@ -474,6 +474,25 @@ var FIELD_PRINTERS = {
474
474
  orderCurrency: (e) => e.orderCurrency ? `Store order currency: ${safeInline(String(e.orderCurrency))}` : null,
475
475
  orderStatus: (e) => e.orderStatus ? `Store order status: ${safeInline(String(e.orderStatus))}` : null,
476
476
  orderPaymentStatus: (e) => e.orderPaymentStatus ? `Store order payment status: ${safeInline(String(e.orderPaymentStatus))}` : null,
477
+ // A refused subscription change (built ahead of the API's production release). Validated by
478
+ // the SDK, printed through safeInline all the same.
479
+ subscriptionStatus: (e) => e.subscriptionStatus ? `Subscription status: ${safeInline(String(e.subscriptionStatus))}` : null,
480
+ refusalReason: (e) => e.refusalReason ? `Refused because: ${safeInline(String(e.refusalReason))}` : null,
481
+ minDate: (e) => e.minDate ? `Earliest date allowed: ${safeInline(String(e.minDate))}` : null,
482
+ maxDate: (e) => e.maxDate ? `Latest date allowed: ${safeInline(String(e.maxDate))}` : null,
483
+ currentAmount: (e) => typeof e.currentAmount === "number" ? `Current highest renewal charge: ${safeInline(String(e.currentAmount))} (minor units)` : null,
484
+ maxAmount: (e) => typeof e.maxAmount === "number" ? `Most this key may make a renewal charge: ${safeInline(String(e.maxAmount))} (minor units)` : e.maxAmount === null ? "Most this key may make a renewal charge: none \u2014 this key may only lower the price" : null,
485
+ amountCurrency: (e) => e.amountCurrency ? `Renewal charge currency: ${safeInline(String(e.amountCurrency))}` : null,
486
+ pendingKind: (e) => e.pendingKind ? `Schedule change waiting for its customer notice: ${safeInline(String(e.pendingKind))}` : null,
487
+ // A charge the payment provider refused before any money moved (built ahead
488
+ // of the API's production release): the failed payment intent that records
489
+ // the attempt. The SDK has checked it is a vpi_* id; printed through
490
+ // safeInline all the same. The next step depends on the code: a card the
491
+ // processor is still storing (payment_method_unvaulted) is retried as is
492
+ // after a short wait; every other refusal needs the request or card fixed.
493
+ // Both under a NEW key: the refused record does not hold the key, and the
494
+ // processor may refuse a resend under the same one.
495
+ paymentIntentId: (e) => e.paymentIntentId ? `Refused attempt recorded as: ${safeInline(String(e.paymentIntentId))} (status failed, nothing was charged; ` + (e.code === "payment_method_unvaulted" ? "wait a few seconds, then send the same charge again with a NEW --idempotency-key)" : "fix the request or the card, then charge with a NEW --idempotency-key)") : null,
477
496
  // On a withheld retry the SDK sets this to RETRY_WITHHELD_HELP.llmHint — the
478
497
  // one wording for every surface — so it is printed as is.
479
498
  llmHint: (e) => e.llmHint ? `Hint: ${str(e.llmHint)}` : null,
@@ -1680,6 +1699,15 @@ var SUPPORTED_EVENTS = [
1680
1699
  "payment_intent.failed",
1681
1700
  "payment_intent.cancelled",
1682
1701
  "mirror.order.created",
1702
+ // Opt-in events (an endpoint gets them only when it lists each by name).
1703
+ // Payloads follow checkout's own "Send test event" samples, which are built
1704
+ // by the same code as the real events.
1705
+ "subscription.created",
1706
+ "subscription.renewed",
1707
+ "subscription.payment_failed",
1708
+ "subscription.cancelled",
1709
+ "subscription.updated",
1710
+ "merchant.card_testing_suspected",
1683
1711
  "session.succeeded",
1684
1712
  "session.failed"
1685
1713
  ];
@@ -1701,6 +1729,57 @@ function warnIfUnsubscribable(event, write = (m) => console.warn(m)) {
1701
1729
  return true;
1702
1730
  }
1703
1731
  var PROCESSOR_CONNECTION = null;
1732
+ function subscriptionEventData(event, paymentIntentId, amount, currency) {
1733
+ const now = Math.floor(Date.now() / 1e3);
1734
+ const inAMonth = now + 30 * 24 * 60 * 60;
1735
+ const base = {
1736
+ subscription_id: `vsub_test_${randomUUID2().replace(/-/g, "").slice(0, 16)}`,
1737
+ status: "active",
1738
+ billing_cycle: null,
1739
+ next_bill_at: inAMonth,
1740
+ amount: null,
1741
+ renewal_amount: amount,
1742
+ currency,
1743
+ payment_intent_id: null,
1744
+ action: null,
1745
+ decline_message: null,
1746
+ decline_code: null,
1747
+ cancel_reason: null,
1748
+ changed_fields: null,
1749
+ // Who made the change (served since checkout's production release of 2026-10-30): an API key
1750
+ // with no id or label on these samples. Set on every event.
1751
+ actor: { type: "api_key", api_key_id: null, label: null },
1752
+ // A cancel scheduled for the end of the paid period, and the discount on future
1753
+ // renewals (served since 2026-10-30): none on these samples.
1754
+ cancel_at_period_end: false,
1755
+ discount: null,
1756
+ // The merchant's own sign-up metadata; `{}` when none was sent.
1757
+ metadata: {}
1758
+ };
1759
+ switch (event) {
1760
+ case "subscription.created":
1761
+ return { ...base, billing_cycle: 1, amount, payment_intent_id: paymentIntentId };
1762
+ case "subscription.renewed":
1763
+ return { ...base, billing_cycle: 2, amount, payment_intent_id: paymentIntentId };
1764
+ case "subscription.payment_failed":
1765
+ return {
1766
+ ...base,
1767
+ status: "past_due",
1768
+ billing_cycle: 2,
1769
+ next_bill_at: now + 3 * 24 * 60 * 60,
1770
+ amount,
1771
+ payment_intent_id: paymentIntentId,
1772
+ // The same three values `charge.failed` carries for this code.
1773
+ action: "soft",
1774
+ decline_message: "The card was declined for insufficient funds.",
1775
+ decline_code: "insufficient_funds"
1776
+ };
1777
+ case "subscription.cancelled":
1778
+ return { ...base, status: "cancelled", next_bill_at: null, cancel_reason: "too_expensive" };
1779
+ case "subscription.updated":
1780
+ return { ...base, renewal_amount: Math.max(1, Math.floor(amount * 0.9)), changed_fields: ["renewal_amount"] };
1781
+ }
1782
+ }
1704
1783
  function buildEventData(event, ids, amount, currency) {
1705
1784
  const base = {
1706
1785
  session_id: ids.sessionId,
@@ -1709,13 +1788,14 @@ function buildEventData(event, ids, amount, currency) {
1709
1788
  amount,
1710
1789
  currency
1711
1790
  };
1791
+ const notARenewal = { subscription_id: null, billing_cycle: null };
1712
1792
  switch (event) {
1713
1793
  case "session.succeeded":
1714
1794
  return base;
1715
1795
  case "payment_intent.succeeded":
1716
- return { ...base, processor_connection: PROCESSOR_CONNECTION };
1796
+ return { ...base, processor_connection: PROCESSOR_CONNECTION, ...notARenewal };
1717
1797
  case "charge.succeeded":
1718
- return { ...base, vp_tx_id: ids.transactionId, card: null, processor_connection: PROCESSOR_CONNECTION };
1798
+ return { ...base, vp_tx_id: ids.transactionId, card: null, processor_connection: PROCESSOR_CONNECTION, ...notARenewal };
1719
1799
  case "session.failed":
1720
1800
  case "payment_intent.failed":
1721
1801
  return {
@@ -1727,8 +1807,8 @@ function buildEventData(event, ids, amount, currency) {
1727
1807
  // could be read (as on this ordinary decline) — never permission to retry.
1728
1808
  network_advice: null,
1729
1809
  network_advice_retry_after_ms: null,
1730
- // payment_intent.failed only: session.failed does not send it.
1731
- ...event === "payment_intent.failed" ? { processor_connection: PROCESSOR_CONNECTION } : {}
1810
+ // payment_intent.failed only: session.failed sends neither.
1811
+ ...event === "payment_intent.failed" ? { processor_connection: PROCESSOR_CONNECTION, ...notARenewal } : {}
1732
1812
  };
1733
1813
  case "charge.failed":
1734
1814
  return {
@@ -1744,7 +1824,8 @@ function buildEventData(event, ids, amount, currency) {
1744
1824
  // Same two fields and values as payment_intent.failed / session.failed.
1745
1825
  network_advice: null,
1746
1826
  network_advice_retry_after_ms: null,
1747
- processor_connection: PROCESSOR_CONNECTION
1827
+ processor_connection: PROCESSOR_CONNECTION,
1828
+ ...notARenewal
1748
1829
  };
1749
1830
  case "mirror.order.created":
1750
1831
  return {
@@ -1753,7 +1834,28 @@ function buildEventData(event, ids, amount, currency) {
1753
1834
  order_status_url: null
1754
1835
  };
1755
1836
  case "payment_intent.cancelled":
1756
- return { ...base, cancellation_reason: "buyer_abandoned", processor_connection: PROCESSOR_CONNECTION };
1837
+ return { ...base, cancellation_reason: "buyer_abandoned", processor_connection: PROCESSOR_CONNECTION, ...notARenewal };
1838
+ case "subscription.created":
1839
+ case "subscription.renewed":
1840
+ case "subscription.payment_failed":
1841
+ case "subscription.cancelled":
1842
+ case "subscription.updated":
1843
+ return subscriptionEventData(event, ids.paymentIntentId, amount, currency);
1844
+ case "merchant.card_testing_suspected": {
1845
+ const now = Math.floor(Date.now() / 1e3);
1846
+ return {
1847
+ patterns: ["expiry_enumeration", "refusal_burst"],
1848
+ window_start: now - 60 * 60,
1849
+ window_end: now,
1850
+ attempts: 30,
1851
+ refused: 24,
1852
+ accepted: 3,
1853
+ distinct_cards: 12,
1854
+ card: { brand: "visa", last4: "4242" },
1855
+ expiry_variants: 9,
1856
+ blocked: false
1857
+ };
1858
+ }
1757
1859
  case "charge.refunded":
1758
1860
  return {
1759
1861
  ...base,
@@ -1768,7 +1870,8 @@ function buildEventData(event, ids, amount, currency) {
1768
1870
  is_partial: false,
1769
1871
  original_charge_amount: amount,
1770
1872
  card: null,
1771
- processor_connection: PROCESSOR_CONNECTION
1873
+ processor_connection: PROCESSOR_CONNECTION,
1874
+ ...notARenewal
1772
1875
  };
1773
1876
  case "refund.failed":
1774
1877
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vonpay/checkout-cli",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Von Payments CLI — developer tools for checkout integration",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,7 @@
20
20
  "node": ">=20"
21
21
  },
22
22
  "dependencies": {
23
- "@vonpay/checkout-node": "^4.3.0",
23
+ "@vonpay/checkout-node": "^4.4.0",
24
24
  "commander": "^13.0.0",
25
25
  "chalk": "^5.4.0",
26
26
  "ora": "^8.2.0"