@peable.to/sdk 0.1.1 → 0.2.1

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 (46) hide show
  1. package/README.md +14 -0
  2. package/dist/cjs/browser/mount.js +55 -8
  3. package/dist/cjs/browser/payerClient.js +58 -8
  4. package/dist/cjs/checkout.js +9 -1
  5. package/dist/cjs/core/client.js +25 -1
  6. package/dist/cjs/core/config.js +11 -0
  7. package/dist/cjs/core/errors.js +1 -1
  8. package/dist/cjs/core/serviceToken.js +11 -2
  9. package/dist/cjs/index.js +28 -1
  10. package/dist/cjs/resources/billing.js +25 -0
  11. package/dist/cjs/resources/checkoutSessions.js +2 -1
  12. package/dist/cjs/resources/connectedAccounts.js +63 -0
  13. package/dist/cjs/resources/merchants.js +51 -0
  14. package/dist/cjs/resources/paymentIntents.js +30 -2
  15. package/dist/cjs/resources/refunds.js +54 -0
  16. package/dist/cjs/resources/transfers.js +49 -0
  17. package/dist/esm/browser/mount.js +52 -7
  18. package/dist/esm/browser/payerClient.js +58 -8
  19. package/dist/esm/checkout.js +5 -0
  20. package/dist/esm/core/client.js +25 -1
  21. package/dist/esm/core/config.js +10 -0
  22. package/dist/esm/core/errors.js +1 -1
  23. package/dist/esm/core/serviceToken.js +11 -2
  24. package/dist/esm/index.js +28 -1
  25. package/dist/esm/resources/billing.js +21 -0
  26. package/dist/esm/resources/checkoutSessions.js +2 -1
  27. package/dist/esm/resources/connectedAccounts.js +59 -0
  28. package/dist/esm/resources/merchants.js +46 -0
  29. package/dist/esm/resources/paymentIntents.js +30 -2
  30. package/dist/esm/resources/refunds.js +50 -0
  31. package/dist/esm/resources/transfers.js +45 -0
  32. package/dist/types/browser/mount.d.ts +23 -1
  33. package/dist/types/browser/payerClient.d.ts +56 -2
  34. package/dist/types/checkout.d.ts +2 -1
  35. package/dist/types/core/client.d.ts +1 -0
  36. package/dist/types/core/config.d.ts +4 -0
  37. package/dist/types/core/errors.d.ts +2 -2
  38. package/dist/types/index.d.ts +40 -1
  39. package/dist/types/resources/billing.d.ts +15 -0
  40. package/dist/types/resources/checkoutSessions.d.ts +15 -1
  41. package/dist/types/resources/connectedAccounts.d.ts +84 -0
  42. package/dist/types/resources/merchants.d.ts +113 -0
  43. package/dist/types/resources/paymentIntents.d.ts +47 -1
  44. package/dist/types/resources/refunds.d.ts +77 -0
  45. package/dist/types/resources/transfers.d.ts +75 -0
  46. package/package.json +2 -2
@@ -0,0 +1,50 @@
1
+ export class RefundsResource {
2
+ constructor(client) {
3
+ this.client = client;
4
+ }
5
+ /**
6
+ * Refund part or all of a payment.
7
+ *
8
+ * **`status` is the refund's own lifecycle and it is not always
9
+ * `succeeded`.** A provider can report a refund `pending` — and a bank can
10
+ * reject one days later, which arrives as an event and moves the row to
11
+ * `failed`. Treating the creation as completion is how a merchant's books
12
+ * come to say money went back that is still with them.
13
+ *
14
+ * `paymentStatus` on the response is where the PAYMENT stands after it, so a
15
+ * caller does not need a second read whose answer can move on before it
16
+ * arrives.
17
+ */
18
+ create(params, options = {}) {
19
+ return this.client.request('POST', '/v1/refunds', { body: params, idempotencyKey: options.idempotencyKey });
20
+ }
21
+ /**
22
+ * What this payment actually came to — gross, fee, net.
23
+ *
24
+ * On this resource because it is the same question you ask in the same
25
+ * breath: how much of this money is really mine.
26
+ *
27
+ * **A missing figure is `null` with a `status`, never `0`.** `unknown` means
28
+ * there is nothing to read — an uncaptured payment, a FairCoin one, a
29
+ * provider that could not be reached — and treating it as zero is how a
30
+ * reconciliation silently gains money that was never there.
31
+ *
32
+ * It reports what the provider took. It does NOT say which entity bears that
33
+ * cost: a fee paid by the operator of a Peable deployment is not
34
+ * automatically your expense, and that is a commercial question this API does
35
+ * not answer.
36
+ */
37
+ settlement(paymentIntentId) {
38
+ return this.client.request('GET', `/v1/payment_intents/${encodeURIComponent(paymentIntentId)}/settlement`);
39
+ }
40
+ /**
41
+ * Every refund against one payment, including ones made OUTSIDE Peable.
42
+ *
43
+ * A refund issued from the acquirer's dashboard, or created by the network
44
+ * resolving a dispute, is imported with `origin: 'provider'` and no
45
+ * `externalRef` — the merchant did not make it and has no id for it.
46
+ */
47
+ listForPaymentIntent(paymentIntentId) {
48
+ return this.client.request('GET', `/v1/payment_intents/${encodeURIComponent(paymentIntentId)}/refunds`);
49
+ }
50
+ }
@@ -0,0 +1,45 @@
1
+ export class TransfersResource {
2
+ constructor(client) {
3
+ this.client = client;
4
+ }
5
+ /**
6
+ * Settle one seller.
7
+ *
8
+ * Answers 201 for a new settlement and 200 for an order already settled,
9
+ * which is the distinction to act on after a timeout: "did I just pay this
10
+ * seller a second time" is a question with a real answer.
11
+ *
12
+ * Refused when the payment cannot fund it — the total settled out of one
13
+ * payment may not exceed what that payment brought in, because the overflow
14
+ * would come from the platform's general balance, which is other merchants'
15
+ * money in flight.
16
+ */
17
+ create(params, options = {}) {
18
+ return this.client.request('POST', '/v1/transfers', { body: params, idempotencyKey: options.idempotencyKey });
19
+ }
20
+ /**
21
+ * Take some or all of a settlement back.
22
+ *
23
+ * The response carries the transfer AND the reversal leg, so a caller can
24
+ * tell which operation it is reading: `amountReversed` on the transfer is the
25
+ * provider's CUMULATIVE figure, while `reversal` is this one.
26
+ */
27
+ reverse(transferId, params, options = {}) {
28
+ if (params.externalRef === undefined && options.idempotencyKey === undefined) {
29
+ // Refused HERE rather than at the gateway, because the failure it
30
+ // prevents is silent: without an identity a retry is a second reversal,
31
+ // and a seller loses the amount twice with nothing saying so.
32
+ throw new TypeError('a reversal needs an identity: pass externalRef, or options.idempotencyKey');
33
+ }
34
+ return this.client.request('POST', `/v1/transfers/${encodeURIComponent(transferId)}/reversals`, {
35
+ body: params,
36
+ ...(options.idempotencyKey === undefined
37
+ ? {}
38
+ : { idempotencyKey: options.idempotencyKey }),
39
+ });
40
+ }
41
+ /** What one payment settled — the reconciliation read. */
42
+ listForPaymentIntent(paymentIntentId) {
43
+ return this.client.request('GET', `/v1/payment_intents/${encodeURIComponent(paymentIntentId)}/transfers`);
44
+ }
45
+ }
@@ -14,11 +14,33 @@ export declare function deriveIntentId(clientSecret: string, intentId?: string):
14
14
  export interface PayDeepLinkParams {
15
15
  intentId: string;
16
16
  clientSecret: string;
17
+ /**
18
+ * The intent's watch-only receive address. FairCoin rail only — this link is
19
+ * a chain payment request and there is nothing to put here for a card.
20
+ */
17
21
  address: string;
18
22
  /** Base-unit integer string (m⊜) — `PaymentIntent['amount']`'s own shape. */
19
23
  amount: string;
20
- network: PaymentIntent['network'];
24
+ network: NonNullable<PaymentIntent['network']>;
21
25
  }
26
+ /**
27
+ * Thrown when a deep link is asked for on an intent that has no chain payment
28
+ * behind it.
29
+ *
30
+ * `address` and `network` became nullable when the card rail landed (gateway
31
+ * ADR 0001 D6), and the tempting fix was a `?? ''` at the call site. That would
32
+ * mint `peable://pay?...&address=&network=` — a link the wallet's parser
33
+ * accepts structurally and which sends a payer to an empty address. Failing
34
+ * here is the only safe direction.
35
+ */
36
+ export declare class PeableRailUnsupportedError extends PeableError {
37
+ constructor(rail: string);
38
+ }
39
+ /**
40
+ * Build the deep link for an intent, refusing any intent that is not a chain
41
+ * payment. The narrowing is what lets `buildPayDeepLink` keep non-null fields.
42
+ */
43
+ export declare function payDeepLinkFor(intent: PaymentIntent): string;
22
44
  /**
23
45
  * Build an `peable://pay?...` deep link. Param names AND percent-encoding
24
46
  * must match the wallet's parser byte-for-byte
@@ -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';
@@ -11,6 +11,7 @@ export interface RestClient {
11
11
  export interface RestClientConfig {
12
12
  /** Gateway base URL, already stripped of a trailing slash. */
13
13
  baseURL: string;
14
+ requestTimeoutMs?: number;
14
15
  }
15
16
  export declare function createRestClient(config: RestClientConfig, tokenProvider: ServiceTokenProvider, deps?: {
16
17
  fetch?: typeof fetch;
@@ -13,6 +13,8 @@ export interface PeableConfig {
13
13
  baseURL?: string;
14
14
  /** oxy-api host used to mint the service token. Default `https://api.oxy.so`. */
15
15
  oxyApiUrl?: string;
16
+ /** Optional deadline per HTTP attempt, including body consumption. */
17
+ requestTimeoutMs?: number;
16
18
  }
17
19
  /** `PeableConfig` with every optional field defaulted and normalized. */
18
20
  export interface ResolvedPeableConfig {
@@ -20,7 +22,9 @@ export interface ResolvedPeableConfig {
20
22
  secret: string;
21
23
  baseURL: string;
22
24
  oxyApiUrl: string;
25
+ requestTimeoutMs?: number;
23
26
  }
27
+ export declare function resolveRequestTimeout(value: number | undefined): number | undefined;
24
28
  /**
25
29
  * Validate and normalize an `PeableConfig`. Throws a plain `Error` for a
26
30
  * missing `publicKey`/`secret` — a programmer error caught at construction
@@ -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,33 @@ 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>;
28
- reject(id: string): Promise<PaymentIntent>;
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
+ */
55
+ reject(id: string, options?: {
56
+ idempotencyKey?: string;
57
+ }): Promise<PaymentIntent>;
58
+ /**
59
+ * What the PAYER's client has to do next — the resume operation.
60
+ *
61
+ * The payment-intent shape carries no confirmation credential, because a
62
+ * credential on that shape would be handed out by every list and every
63
+ * re-read. This is the bounded way to get one: a single payment, named
64
+ * explicitly, for a caller who has proven they may pay it.
65
+ *
66
+ * Call it to resume an unpaid checkout rather than creating a second payment
67
+ * — which is what a buyer returning the next day would otherwise produce, and
68
+ * an idempotency key that has aged out at the provider will not prevent.
69
+ *
70
+ * @throws {PeableInvalidRequestError} (409) when the payment can no longer be
71
+ * paid, so a surface handed this can never render a card form over a
72
+ * settled payment.
73
+ */
74
+ clientAction(id: string): Promise<PaymentIntentClientAction>;
29
75
  }