@peable.to/sdk 0.1.1 → 0.2.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 (41) hide show
  1. package/dist/cjs/browser/mount.js +55 -8
  2. package/dist/cjs/browser/payerClient.js +58 -8
  3. package/dist/cjs/checkout.js +9 -1
  4. package/dist/cjs/core/client.js +22 -1
  5. package/dist/cjs/core/errors.js +1 -1
  6. package/dist/cjs/core/serviceToken.js +1 -1
  7. package/dist/cjs/index.js +27 -0
  8. package/dist/cjs/resources/billing.js +25 -0
  9. package/dist/cjs/resources/checkoutSessions.js +2 -1
  10. package/dist/cjs/resources/connectedAccounts.js +63 -0
  11. package/dist/cjs/resources/merchants.js +51 -0
  12. package/dist/cjs/resources/paymentIntents.js +28 -0
  13. package/dist/cjs/resources/refunds.js +54 -0
  14. package/dist/cjs/resources/transfers.js +49 -0
  15. package/dist/esm/browser/mount.js +52 -7
  16. package/dist/esm/browser/payerClient.js +58 -8
  17. package/dist/esm/checkout.js +5 -0
  18. package/dist/esm/core/client.js +22 -1
  19. package/dist/esm/core/errors.js +1 -1
  20. package/dist/esm/core/serviceToken.js +1 -1
  21. package/dist/esm/index.js +27 -0
  22. package/dist/esm/resources/billing.js +21 -0
  23. package/dist/esm/resources/checkoutSessions.js +2 -1
  24. package/dist/esm/resources/connectedAccounts.js +59 -0
  25. package/dist/esm/resources/merchants.js +46 -0
  26. package/dist/esm/resources/paymentIntents.js +28 -0
  27. package/dist/esm/resources/refunds.js +50 -0
  28. package/dist/esm/resources/transfers.js +45 -0
  29. package/dist/types/browser/mount.d.ts +23 -1
  30. package/dist/types/browser/payerClient.d.ts +56 -2
  31. package/dist/types/checkout.d.ts +2 -1
  32. package/dist/types/core/errors.d.ts +2 -2
  33. package/dist/types/index.d.ts +40 -1
  34. package/dist/types/resources/billing.d.ts +15 -0
  35. package/dist/types/resources/checkoutSessions.d.ts +15 -1
  36. package/dist/types/resources/connectedAccounts.d.ts +84 -0
  37. package/dist/types/resources/merchants.d.ts +113 -0
  38. package/dist/types/resources/paymentIntents.d.ts +44 -0
  39. package/dist/types/resources/refunds.d.ts +75 -0
  40. package/dist/types/resources/transfers.d.ts +73 -0
  41. package/package.json +2 -2
@@ -1,4 +1,38 @@
1
1
  import type { PaymentIntent } from '@peable.to/shared-types';
2
+ /**
3
+ * Whether updates are actually arriving.
4
+ *
5
+ * `'live'` means the socket is connected AND rejoined to this intent's room —
6
+ * both, because a connected socket in no room delivers nothing and is
7
+ * indistinguishable from a quiet payment.
8
+ *
9
+ * `'lost'` is a report, not a recovery: socket.io keeps retrying the transport
10
+ * on its own, but it replays NOTHING, so anything emitted during the gap is
11
+ * gone. A host that wants those updates has to re-read the intent when `'live'`
12
+ * comes back.
13
+ */
14
+ export type RealtimeConnectionState = 'live' | 'lost';
15
+ /**
16
+ * What the payer's client has to do next — the RESUME capability.
17
+ *
18
+ * The payment-intent DTO deliberately carries no confirmation credential, so a
19
+ * payer who refreshed the page, came back from an SCA challenge in a new tab,
20
+ * or returned the next day had nothing to pay WITH. This is the one call that
21
+ * hands one out, bounded to a single payment the caller has proven they may
22
+ * pay.
23
+ *
24
+ * `value` is opaque and provider-specific; `publishableKey` is the provider's
25
+ * public key, served here rather than compiled into a checkout bundle — that
26
+ * page is deployed once and serves whichever gateway it is pointed at, so a
27
+ * baked-in key would be the wrong mode the first time a test deployment used
28
+ * the same page.
29
+ */
30
+ export interface PeableClientAction {
31
+ readonly object: 'client_action';
32
+ readonly kind: 'client_secret' | 'redirect';
33
+ readonly value: string;
34
+ readonly publishableKey?: string;
35
+ }
2
36
  export interface PeableCheckoutClient {
3
37
  /**
4
38
  * `GET /v1/payment_intents/:id` — the initial REST snapshot. Sends
@@ -6,12 +40,32 @@ export interface PeableCheckoutClient {
6
40
  * param, so the capability token never lands in server/CDN access logs.
7
41
  */
8
42
  getPaymentIntent(id: string, clientSecret: string): Promise<PaymentIntent>;
9
- /** Realtime status stream over the Gateway's socket contract, filtered to this intent. */
10
- subscribe(id: string, clientSecret: string, onUpdate: (intent: PaymentIntent) => void): Promise<{
43
+ /**
44
+ * Realtime status stream over the Gateway's socket contract, filtered to this
45
+ * intent.
46
+ *
47
+ * `onConnectionChange` is optional and additive: it reports drops and
48
+ * recoveries AFTER this promise resolves, which nothing could observe before.
49
+ * A caller that omits it behaves exactly as it did.
50
+ */
51
+ subscribe(id: string, clientSecret: string, onUpdate: (intent: PaymentIntent) => void, onConnectionChange?: (state: RealtimeConnectionState) => void): Promise<{
11
52
  unsubscribe(): void;
12
53
  }>;
13
54
  /** `POST /v1/payment_intents/:id/submit_tx` — report a broadcast txid. */
14
55
  submitTx(id: string, clientSecret: string, txid: string): Promise<PaymentIntent>;
56
+ /**
57
+ * `POST /v1/payment_intents/:id/client_action` — what to pay WITH.
58
+ *
59
+ * A POST, and not a field on `getPaymentIntent`, because the answer is a
60
+ * credential: a GET response is cacheable by anything in front of it and its
61
+ * path lands in access logs, while `getPaymentIntent` is the call a checkout
62
+ * page POLLS.
63
+ *
64
+ * @throws {PeableInvalidRequestError} when the payment can no longer be paid
65
+ * — a settled, expired or rejected payment has no next step, and a page
66
+ * handed one would render a card form over a finished payment.
67
+ */
68
+ getClientAction(id: string, clientSecret: string): Promise<PeableClientAction>;
15
69
  }
16
70
  export interface CreatePeableCheckoutOptions {
17
71
  /** Gateway base URL. Default `https://api.peable.to`. */
@@ -1,2 +1,3 @@
1
- export { createPeableCheckout, type CreatePeableCheckoutOptions, type PeableCheckoutClient, } from './browser/payerClient';
1
+ export { createPeableCheckout, type CreatePeableCheckoutOptions, type PeableCheckoutClient, type PeableClientAction, type RealtimeConnectionState, } from './browser/payerClient';
2
2
  export { PeableCheckout, type PeableCheckoutMountOptions, type PeableCheckoutInstance, type PeableCheckoutEventMap, } from './browser/mount';
3
+ export { payDeepLinkFor, buildPayDeepLink, type PayDeepLinkParams, PeableRailUnsupportedError, } from './browser/mount';
@@ -5,7 +5,7 @@ export interface PeableErrorDetails {
5
5
  /**
6
6
  * The raw upstream discriminator: `error.type` from the Gateway's own
7
7
  * `{ error: { type, message } }` envelope, or the flat `error` code string
8
- * from `@oxyhq/core`'s auth middleware (`{ error: 'INVALID_TOKEN', ... }`)
8
+ * from `@oxy.so/core`'s auth middleware (`{ error: 'INVALID_TOKEN', ... }`)
9
9
  * — see `errorFromResponse`'s doc comment for why both shapes exist.
10
10
  */
11
11
  code?: string;
@@ -39,7 +39,7 @@ export declare class PeableSignatureVerificationError extends PeableError {
39
39
  * request was rejected:
40
40
  * - The Gateway's own route handlers use the Stripe-style nested envelope
41
41
  * `{ error: { type, message } }` (`lib/http.ts`'s `sendError`).
42
- * - `@oxyhq/core`'s Express auth middleware (`oxyClient.serviceAuth()` /
42
+ * - `@oxy.so/core`'s Express auth middleware (`oxy.middleware.service()` /
43
43
  * `requireScope()` — rejecting a missing/expired/insufficiently-scoped
44
44
  * service token BEFORE a route handler ever runs) and oxy-api's own
45
45
  * `ApiError.toJSON()` use a FLAT envelope: `{ error: <CODE_STRING>,
@@ -1,13 +1,47 @@
1
+ import { BillingResource } from './resources/billing';
2
+ export type { BillingRequestOptions } from './resources/billing';
3
+ export type { BillingCustomer, BillingHostedSession, BillingSubscription, CreateBillingCheckoutParams, CreateBillingPortalParams, EnsureBillingCustomerParams } from '@peable.to/shared-types';
1
4
  import { type PeableConfig } from './core/config';
2
5
  import { PaymentIntentsResource } from './resources/paymentIntents';
3
6
  import { PaymentLinksResource } from './resources/paymentLinks';
4
7
  import { CheckoutResource } from './resources/checkoutSessions';
5
8
  import { WebhooksResource } from './resources/webhooks';
9
+ import { ConnectedAccountsResource } from './resources/connectedAccounts';
10
+ import { DisputesResource, MerchantsResource } from './resources/merchants';
11
+ import { RefundsResource } from './resources/refunds';
12
+ import { TransfersResource } from './resources/transfers';
13
+ /**
14
+ * The merchant-authed client.
15
+ *
16
+ * ## Why the namespaces below are not optional extras
17
+ *
18
+ * This class published four: intents, links, checkout and webhooks. The gateway
19
+ * has always served merchants, connected accounts, refunds, transfers and
20
+ * disputes as well — so every integrator reaching those wrote their own HTTP
21
+ * client and their own partial types for responses this package already knew.
22
+ * Mercaria did exactly that. Two descriptions of one wire format, in two
23
+ * repositories, with nothing comparing them: a field renamed in the gateway is
24
+ * a runtime failure over there, found by a settlement that does not happen.
25
+ *
26
+ * An SDK that covers less than the API it wraps does not reduce surface, it
27
+ * relocates it.
28
+ */
6
29
  export declare class Peable {
30
+ readonly billing: BillingResource;
7
31
  readonly paymentIntents: PaymentIntentsResource;
8
32
  readonly paymentLinks: PaymentLinksResource;
9
33
  readonly checkout: CheckoutResource;
10
34
  readonly webhooks: WebhooksResource;
35
+ /** The merchant this credential speaks for. Resolved from the credential. */
36
+ readonly merchants: MerchantsResource;
37
+ /** Sellers a marketplace onboards, and their readiness. */
38
+ readonly connectedAccounts: ConnectedAccountsResource;
39
+ /** Money going back to a payer. */
40
+ readonly refunds: RefundsResource;
41
+ /** Money going out to a seller, and coming back from one. */
42
+ readonly transfers: TransfersResource;
43
+ /** Read-only: the network initiates these. */
44
+ readonly disputes: DisputesResource;
11
45
  constructor(config: PeableConfig);
12
46
  }
13
47
  export type { PeableConfig, ResolvedPeableConfig } from './core/config';
@@ -16,7 +50,12 @@ export { PeableError, PeableAuthenticationError, PeableInvalidRequestError, Peab
16
50
  export type { PeableErrorType, PeableErrorDetails } from './core/errors';
17
51
  export type { RestClient, RestClientRequestOptions } from './core/client';
18
52
  export type { ServiceTokenProvider } from './core/serviceToken';
19
- export type { CreatePaymentIntentOptions, PaymentIntentListParams, PaymentIntentList, } from './resources/paymentIntents';
53
+ export type { CreateCheckoutSessionOptions } from './resources/checkoutSessions';
54
+ export type { CreatePaymentIntentOptions, PaymentIntentClientAction, PaymentIntentListParams, PaymentIntentList, } from './resources/paymentIntents';
55
+ export type { AccountLink, AccountLinkParams, ConnectedAccountList, ConnectedAccountListParams, CreateConnectedAccountParams, } from './resources/connectedAccounts';
56
+ export type { DisputeEvidenceParams, DisputeList, RegisterMerchantParams, UpdateMerchantParams, } from './resources/merchants';
57
+ export type { CreateRefundParams, RefundList } from './resources/refunds';
58
+ export type { CreateTransferParams, ReverseTransferParams, TransferList, } from './resources/transfers';
20
59
  export type { PaymentLinkListParams, PaymentLinkList, UpdatePaymentLinkParams, } from './resources/paymentLinks';
21
60
  export { WEBHOOK_SIGNATURE_HEADER } from './resources/webhooks';
22
61
  export type { ConstructEventOptions } from './resources/webhooks';
@@ -0,0 +1,15 @@
1
+ import type { BillingCustomer, BillingHostedSession, BillingSubscription, CreateBillingCheckoutParams, CreateBillingPortalParams, EnsureBillingCustomerParams } from '@peable.to/shared-types';
2
+ import type { RestClient } from '../core/client';
3
+ export interface BillingRequestOptions {
4
+ idempotencyKey: string;
5
+ }
6
+ /** Recurring billing is separate from one-off checkout and marketplace transfers. */
7
+ export declare class BillingResource {
8
+ private readonly client;
9
+ constructor(client: RestClient);
10
+ ensureCustomer(params: EnsureBillingCustomerParams, options: BillingRequestOptions): Promise<BillingCustomer>;
11
+ createCheckoutSession(params: CreateBillingCheckoutParams, options: BillingRequestOptions): Promise<BillingHostedSession>;
12
+ createPortalSession(params: CreateBillingPortalParams, options: BillingRequestOptions): Promise<BillingHostedSession>;
13
+ retrieveSubscription(ref: string): Promise<BillingSubscription>;
14
+ cancelAtPeriodEnd(ref: string, options: BillingRequestOptions): Promise<BillingSubscription>;
15
+ }
@@ -1,9 +1,23 @@
1
1
  import type { CheckoutSession, CreateCheckoutSessionParams } from '@peable.to/shared-types';
2
2
  import type { RestClient } from '../core/client';
3
+ export interface CreateCheckoutSessionOptions {
4
+ /**
5
+ * Sent as `Idempotency-Key`. A create that timed out does not tell the caller
6
+ * whether the session exists; retrying with the SAME key returns the session
7
+ * that create made (HTTP 200) instead of minting a second one and a second
8
+ * payment intent, and the same key with different parameters is refused with
9
+ * a 409 (`PeableInvalidRequestError`). Use one stable key per order, not one
10
+ * per attempt.
11
+ *
12
+ * Optional, because the gateway has never required it on this route — but an
13
+ * integration without it can create two live checkouts for one order.
14
+ */
15
+ idempotencyKey?: string;
16
+ }
3
17
  export declare class CheckoutSessionsResource {
4
18
  private readonly client;
5
19
  constructor(client: RestClient);
6
- create(params: CreateCheckoutSessionParams): Promise<CheckoutSession>;
20
+ create(params: CreateCheckoutSessionParams, options?: CreateCheckoutSessionOptions): Promise<CheckoutSession>;
7
21
  retrieve(id: string): Promise<CheckoutSession>;
8
22
  }
9
23
  /** `peable.checkout.sessions.*` — a thin namespace wrapper so the public API
@@ -0,0 +1,84 @@
1
+ import type { ConnectedAccount } from '@peable.to/shared-types';
2
+ import type { RestClient } from '../core/client';
3
+ /**
4
+ * Sellers a marketplace onboards through Peable.
5
+ *
6
+ * The provider's own account id is not on any of these shapes, and never will
7
+ * be (ADR 0001 D3): a merchant integrates against Peable and does not learn
8
+ * which acquirer sits behind their seller, because the day that changes should
9
+ * be a Peable deploy and not a merchant migration.
10
+ *
11
+ * Every account is addressed by the merchant's OWN reference as well as by its
12
+ * `ca_…`. That is deliberate and it is what makes recovery possible: a create
13
+ * whose response never arrived leaves the merchant without the `ca_…` and still
14
+ * holding the reference they chose.
15
+ */
16
+ export interface CreateConnectedAccountParams {
17
+ /** The merchant's own id for this seller. The idempotency, and durable. */
18
+ externalRef: string;
19
+ /** ISO 3166-1 alpha-2. Case-insensitive; the gateway upper-cases it. */
20
+ country: string;
21
+ businessType: 'individual' | 'company';
22
+ }
23
+ export interface ConnectedAccountListParams {
24
+ limit?: number;
25
+ /** A `ca_…` from a previous page — the gateway resolves it, scoped to you. */
26
+ starting_after?: string;
27
+ }
28
+ export interface ConnectedAccountList {
29
+ object: 'list';
30
+ data: ConnectedAccount[];
31
+ has_more: boolean;
32
+ }
33
+ export interface AccountLinkParams {
34
+ /** Where the provider sends a seller whose link expired mid-flow. */
35
+ refreshUrl: string;
36
+ returnUrl: string;
37
+ }
38
+ export interface AccountLink {
39
+ object: 'account_link';
40
+ url: string;
41
+ expiresAt: string;
42
+ }
43
+ export declare class ConnectedAccountsResource {
44
+ private readonly client;
45
+ constructor(client: RestClient);
46
+ /**
47
+ * Open the account for a seller, or return the one they already have.
48
+ *
49
+ * Converges rather than erroring, because the underlying object CANNOT BE
50
+ * DELETED: opening two for one seller leaves them with two forever, one of
51
+ * which nobody uses and which keeps generating requirement emails.
52
+ */
53
+ create(params: CreateConnectedAccountParams): Promise<ConnectedAccount>;
54
+ list(params?: ConnectedAccountListParams): Promise<ConnectedAccountList>;
55
+ retrieve(accountId: string): Promise<ConnectedAccount>;
56
+ /**
57
+ * By the merchant's OWN reference.
58
+ *
59
+ * The recovery read: a merchant who lost the `ca_…` — a create whose response
60
+ * never arrived — still has the reference they chose, and this is what makes
61
+ * finding the account possible without a list scan.
62
+ */
63
+ retrieveByRef(externalRef: string): Promise<ConnectedAccount>;
64
+ /**
65
+ * Re-read this account from the provider now.
66
+ *
67
+ * Rarely needed: readiness arrives on its own through the gateway's event
68
+ * drain and a backstop sweep. This is for a seller staring at a dashboard who
69
+ * has just finished onboarding.
70
+ */
71
+ refresh(accountId: string): Promise<ConnectedAccount>;
72
+ /**
73
+ * A hosted onboarding link.
74
+ *
75
+ * SINGLE-USE and short-lived at the provider, so it is minted on demand and
76
+ * must never be stored, emailed or put in a chat message — a stored link is
77
+ * one that has already expired by the time anyone follows it, and the failure
78
+ * looks like the seller's fault.
79
+ *
80
+ * A visit to `returnUrl` proves NOTHING about verification. Read the account
81
+ * back, or wait for the readiness event.
82
+ */
83
+ createAccountLink(accountId: string, params: AccountLinkParams): Promise<AccountLink>;
84
+ }
@@ -0,0 +1,113 @@
1
+ import type { Dispute, DisputeEvidence, Merchant } from '@peable.to/shared-types';
2
+ import type { RestClient } from '../core/client';
3
+ /**
4
+ * The merchant this credential speaks for, and the disputes raised against it.
5
+ *
6
+ * ## One merchant per application PER ENVIRONMENT
7
+ *
8
+ * There is no merchant id in any of these calls. The gateway resolves the
9
+ * merchant from the credential — its Oxy application AND that credential's
10
+ * environment — so a development credential and a production one address
11
+ * different merchants and cannot be made to address each other's.
12
+ *
13
+ * ## The FairCoin half is optional
14
+ *
15
+ * `network` and `xpub` are two halves of ONE capability and are supplied
16
+ * together or not at all. A merchant that only takes cards registers with
17
+ * neither; supplying a watch-only key for a chain they never intend to use
18
+ * would mean custodying a real key or, worse, registering a fixture that
19
+ * silently makes their receive addresses underivable by their own wallet.
20
+ *
21
+ * Peable never accepts a PRIVATE extended key. The gateway derives a child from
22
+ * whatever it is given and refuses anything that can spend — that refusal is
23
+ * the non-custody firewall, not a validation nicety.
24
+ */
25
+ export interface RegisterMerchantParams {
26
+ /** Both, or neither. Omit both for a card-only merchant. */
27
+ network?: 'mainnet' | 'testnet';
28
+ /** A WATCH-ONLY account extended public key. An `xprv` is refused. */
29
+ xpub?: string;
30
+ webhookUrl?: string;
31
+ webhookSecret?: string;
32
+ requiredConfirmations?: number;
33
+ displayName?: string;
34
+ /** A bare Oxy file id, never a URL. */
35
+ avatarFileId?: string;
36
+ description?: string;
37
+ }
38
+ /**
39
+ * `null` CLEARS a field; omitting it leaves it alone.
40
+ *
41
+ * The distinction is by `undefined` rather than by falsiness, so a webhook URL
42
+ * can be removed rather than only replaced.
43
+ */
44
+ export interface UpdateMerchantParams {
45
+ webhookUrl?: string | null;
46
+ webhookSecret?: string | null;
47
+ requiredConfirmations?: number;
48
+ displayName?: string | null;
49
+ avatarFileId?: string | null;
50
+ description?: string | null;
51
+ }
52
+ export interface DisputeList {
53
+ object: 'list';
54
+ data: Dispute[];
55
+ }
56
+ export declare class MerchantsResource {
57
+ private readonly client;
58
+ constructor(client: RestClient);
59
+ register(params?: RegisterMerchantParams): Promise<Merchant>;
60
+ retrieve(): Promise<Merchant>;
61
+ update(params: UpdateMerchantParams): Promise<Merchant>;
62
+ }
63
+ /**
64
+ * Evidence — TEXT only, in the gateway's field names.
65
+ *
66
+ * Every field is your own words or your own record. They are forwarded to the
67
+ * card network and **never stored by Peable**: they carry a customer's name,
68
+ * their email, a billing address and correspondence, and the gateway keeps none
69
+ * of that. What it records is THAT you responded and when.
70
+ *
71
+ * FILE attachments are not supported. They need the acquirer's upload API and a
72
+ * policy for the bytes on the way through; offering half of it would let you
73
+ * submit — finally — a defence missing the receipt it rests on.
74
+ *
75
+ * An unknown key is REFUSED rather than dropped, so a misspelled field is a 422
76
+ * now instead of a missing argument discovered when the dispute is decided.
77
+ */
78
+ /**
79
+ * The evidence set, re-exported from the wire contract rather than restated.
80
+ *
81
+ * It was written out here as well as in the gateway's provider port, its route
82
+ * schema and its Stripe adapter — four copies of one list. A field added to
83
+ * three of them is a field you send, the gateway accepts, and the network never
84
+ * sees; you would find out when the dispute is decided against you.
85
+ */
86
+ export type DisputeEvidenceParams = DisputeEvidence;
87
+ /**
88
+ * Disputes: read them, and answer one.
89
+ *
90
+ * A dispute is initiated by the card NETWORK, not by you, so there is nothing
91
+ * to create — the only write is a response. This surface was read-only for a
92
+ * while, because the gateway had no way to deliver evidence and a route that
93
+ * accepted it and did nothing would have been worse than none: you would have
94
+ * believed you had responded.
95
+ */
96
+ export declare class DisputesResource {
97
+ private readonly client;
98
+ constructor(client: RestClient);
99
+ listForPaymentIntent(paymentIntentId: string): Promise<DisputeList>;
100
+ /**
101
+ * Answer a dispute. **One shot.**
102
+ *
103
+ * Submitting is one-way at the card network, so this cannot be revised and a
104
+ * second call does not send a second response — it answers 200 with the
105
+ * dispute as it stands. Send everything you have the first time.
106
+ *
107
+ * Refused (409) when the dispute is already answered, already decided, or
108
+ * past `evidenceDueAt`. That last one is checked by the gateway rather than
109
+ * by the acquirer, so you learn it was the clock and not your request — a
110
+ * late response is the one failure here that no retry fixes.
111
+ */
112
+ submitEvidence(disputeId: string, evidence: DisputeEvidenceParams): Promise<Dispute>;
113
+ }
@@ -10,6 +10,24 @@ export interface PaymentIntentList {
10
10
  data: PaymentIntent[];
11
11
  has_more: boolean;
12
12
  }
13
+ /**
14
+ * The payer's next step, as the gateway reports it.
15
+ *
16
+ * `value` is opaque and provider-specific — a confirmation credential or a
17
+ * redirect target. Do not store it, do not log it, and do not put it in a URL:
18
+ * it is a capability over the payment at the acquirer, and it is read live from
19
+ * the provider precisely so nothing here has to keep one.
20
+ *
21
+ * `publishableKey` is the provider's PUBLIC key, served here rather than
22
+ * configured in the client, so a page that talks to more than one deployment
23
+ * cannot mount a live card form over a test payment.
24
+ */
25
+ export interface PaymentIntentClientAction {
26
+ object: 'client_action';
27
+ kind: 'client_secret' | 'redirect';
28
+ value: string;
29
+ publishableKey?: string;
30
+ }
13
31
  export interface CreatePaymentIntentOptions {
14
32
  /**
15
33
  * Required — the Gateway mandates the `Idempotency-Key` header on
@@ -25,5 +43,31 @@ export declare class PaymentIntentsResource {
25
43
  create(params: CreatePaymentIntentParams, options: CreatePaymentIntentOptions): Promise<PaymentIntent>;
26
44
  retrieve(id: string): Promise<PaymentIntent>;
27
45
  list(params?: PaymentIntentListParams): Promise<PaymentIntentList>;
46
+ /**
47
+ * Reject a payment the payer has not completed.
48
+ *
49
+ * The gateway CANCELS it at the provider before it announces anything, so
50
+ * this can lose a race and say so: a 409 means the payer confirmed first and
51
+ * the payment is settled, and a 502 means the provider could not be reached
52
+ * and nothing was decided. Neither is a failure of the request — both are
53
+ * facts about a payment that is still live.
54
+ */
28
55
  reject(id: string): Promise<PaymentIntent>;
56
+ /**
57
+ * What the PAYER's client has to do next — the resume operation.
58
+ *
59
+ * The payment-intent shape carries no confirmation credential, because a
60
+ * credential on that shape would be handed out by every list and every
61
+ * re-read. This is the bounded way to get one: a single payment, named
62
+ * explicitly, for a caller who has proven they may pay it.
63
+ *
64
+ * Call it to resume an unpaid checkout rather than creating a second payment
65
+ * — which is what a buyer returning the next day would otherwise produce, and
66
+ * an idempotency key that has aged out at the provider will not prevent.
67
+ *
68
+ * @throws {PeableInvalidRequestError} (409) when the payment can no longer be
69
+ * paid, so a surface handed this can never render a card form over a
70
+ * settled payment.
71
+ */
72
+ clientAction(id: string): Promise<PaymentIntentClientAction>;
29
73
  }
@@ -0,0 +1,75 @@
1
+ import type { Refund, Settlement } from '@peable.to/shared-types';
2
+ import type { RestClient } from '../core/client';
3
+ /**
4
+ * Sending money back to a payer.
5
+ *
6
+ * The one operation on this API whose duplicate is unrecoverable: a payer sent
7
+ * their money twice has no reason to report it, and nothing reverses the second
8
+ * automatically. `externalRef` — your own refund id — is what every path here
9
+ * converges on, and unlike a header key you cannot lose it.
10
+ */
11
+ export interface CreateRefundParams {
12
+ /** The `pi_…` being refunded. */
13
+ paymentIntentId: string;
14
+ /** YOUR id for this refund. The idempotency. */
15
+ externalRef: string;
16
+ /** Minor units, as a canonical integer string. Zero is refused. */
17
+ amount: string;
18
+ }
19
+ export interface RefundList {
20
+ object: 'list';
21
+ data: Refund[];
22
+ /**
23
+ * What can still go back, in minor units.
24
+ *
25
+ * Offered because otherwise every integrator computes it by summing the list
26
+ * — and the ones who forget that a `pending` or `failed` refund moved no
27
+ * money compute it wrong, in the direction that refuses a legitimate refund.
28
+ * It reserves PENDING refunds, which is what stops two concurrent ones
29
+ * between them exceeding the payment.
30
+ */
31
+ remainingRefundable: string;
32
+ }
33
+ export declare class RefundsResource {
34
+ private readonly client;
35
+ constructor(client: RestClient);
36
+ /**
37
+ * Refund part or all of a payment.
38
+ *
39
+ * **`status` is the refund's own lifecycle and it is not always
40
+ * `succeeded`.** A provider can report a refund `pending` — and a bank can
41
+ * reject one days later, which arrives as an event and moves the row to
42
+ * `failed`. Treating the creation as completion is how a merchant's books
43
+ * come to say money went back that is still with them.
44
+ *
45
+ * `paymentStatus` on the response is where the PAYMENT stands after it, so a
46
+ * caller does not need a second read whose answer can move on before it
47
+ * arrives.
48
+ */
49
+ create(params: CreateRefundParams): Promise<Refund>;
50
+ /**
51
+ * What this payment actually came to — gross, fee, net.
52
+ *
53
+ * On this resource because it is the same question you ask in the same
54
+ * breath: how much of this money is really mine.
55
+ *
56
+ * **A missing figure is `null` with a `status`, never `0`.** `unknown` means
57
+ * there is nothing to read — an uncaptured payment, a FairCoin one, a
58
+ * provider that could not be reached — and treating it as zero is how a
59
+ * reconciliation silently gains money that was never there.
60
+ *
61
+ * It reports what the provider took. It does NOT say which entity bears that
62
+ * cost: a fee paid by the operator of a Peable deployment is not
63
+ * automatically your expense, and that is a commercial question this API does
64
+ * not answer.
65
+ */
66
+ settlement(paymentIntentId: string): Promise<Settlement>;
67
+ /**
68
+ * Every refund against one payment, including ones made OUTSIDE Peable.
69
+ *
70
+ * A refund issued from the acquirer's dashboard, or created by the network
71
+ * resolving a dispute, is imported with `origin: 'provider'` and no
72
+ * `externalRef` — the merchant did not make it and has no id for it.
73
+ */
74
+ listForPaymentIntent(paymentIntentId: string): Promise<RefundList>;
75
+ }
@@ -0,0 +1,73 @@
1
+ import type { Transfer, TransferWithReversal } from '@peable.to/shared-types';
2
+ import type { RestClient } from '../core/client';
3
+ /**
4
+ * Settling a seller out of a funded payment, and taking it back.
5
+ *
6
+ * Two rules govern this whole surface and neither is negotiable:
7
+ *
8
+ * 1. **The amount comes from you.** Peable does not compute a marketplace's
9
+ * split and must never learn its fee schedule; it records that a stated
10
+ * amount was moved. A gateway that computed the split would be a second
11
+ * definition of it.
12
+ * 2. **Your own id is the idempotency.** `externalRef` is your order id, and
13
+ * unlike a header key you cannot lose it — a retried settlement converges on
14
+ * it rather than paying a seller twice.
15
+ */
16
+ export interface CreateTransferParams {
17
+ /** The `pi_…` this settles out of. Must be a SETTLED payment. */
18
+ paymentIntentId: string;
19
+ /** Name the seller by exactly one of these. */
20
+ connectedAccountId?: string;
21
+ connectedAccountRef?: string;
22
+ /** YOUR id for what this settles. The idempotency, and it is durable. */
23
+ externalRef: string;
24
+ /** Minor units, as a canonical integer string. In the payment's currency. */
25
+ amount: string;
26
+ }
27
+ export interface ReverseTransferParams {
28
+ amount: string;
29
+ /**
30
+ * YOUR id for THIS reversal — required, one way or the other.
31
+ *
32
+ * Supply it here, or as `options.idempotencyKey`, which the gateway accepts
33
+ * as the same identity. It cannot be omitted, and an AMOUNT IS NOT AN
34
+ * IDENTITY: two reversals of one settlement for the same amount are two
35
+ * distinct operations, and a gateway that keyed them by amount would answer
36
+ * the first one to the second request — leaving a seller holding money that
37
+ * had been taken back, with nothing recording that it was asked for.
38
+ */
39
+ externalRef?: string;
40
+ }
41
+ export interface TransferList {
42
+ object: 'list';
43
+ data: Transfer[];
44
+ }
45
+ export declare class TransfersResource {
46
+ private readonly client;
47
+ constructor(client: RestClient);
48
+ /**
49
+ * Settle one seller.
50
+ *
51
+ * Answers 201 for a new settlement and 200 for an order already settled,
52
+ * which is the distinction to act on after a timeout: "did I just pay this
53
+ * seller a second time" is a question with a real answer.
54
+ *
55
+ * Refused when the payment cannot fund it — the total settled out of one
56
+ * payment may not exceed what that payment brought in, because the overflow
57
+ * would come from the platform's general balance, which is other merchants'
58
+ * money in flight.
59
+ */
60
+ create(params: CreateTransferParams): Promise<Transfer>;
61
+ /**
62
+ * Take some or all of a settlement back.
63
+ *
64
+ * The response carries the transfer AND the reversal leg, so a caller can
65
+ * tell which operation it is reading: `amountReversed` on the transfer is the
66
+ * provider's CUMULATIVE figure, while `reversal` is this one.
67
+ */
68
+ reverse(transferId: string, params: ReverseTransferParams, options?: {
69
+ idempotencyKey?: string;
70
+ }): Promise<TransferWithReversal>;
71
+ /** What one payment settled — the reconciliation read. */
72
+ listForPaymentIntent(paymentIntentId: string): Promise<TransferList>;
73
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peable.to/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Peable SDK — a Stripe-ergonomics TypeScript client over the Peable Gateway REST + realtime contract. Server entry (`@peable.to/sdk`) mints Oxy service tokens from an ApplicationCredential and exposes paymentIntents/paymentLinks/checkout.sessions/webhooks; browser entry (`@peable.to/sdk/checkout`) is the anonymous payer-side client core.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -71,7 +71,7 @@
71
71
  "prepublishOnly": "bun run clean && bun run build"
72
72
  },
73
73
  "dependencies": {
74
- "@peable.to/shared-types": "workspace:^",
74
+ "@peable.to/shared-types": "^0.3.0",
75
75
  "qrcode-generator": "^2.0.4",
76
76
  "socket.io-client": "^4.8.3"
77
77
  },