@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.
- package/dist/cjs/browser/mount.js +55 -8
- package/dist/cjs/browser/payerClient.js +58 -8
- package/dist/cjs/checkout.js +9 -1
- package/dist/cjs/core/client.js +22 -1
- package/dist/cjs/core/errors.js +1 -1
- package/dist/cjs/core/serviceToken.js +1 -1
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/resources/billing.js +25 -0
- package/dist/cjs/resources/checkoutSessions.js +2 -1
- package/dist/cjs/resources/connectedAccounts.js +63 -0
- package/dist/cjs/resources/merchants.js +51 -0
- package/dist/cjs/resources/paymentIntents.js +28 -0
- package/dist/cjs/resources/refunds.js +54 -0
- package/dist/cjs/resources/transfers.js +49 -0
- package/dist/esm/browser/mount.js +52 -7
- package/dist/esm/browser/payerClient.js +58 -8
- package/dist/esm/checkout.js +5 -0
- package/dist/esm/core/client.js +22 -1
- package/dist/esm/core/errors.js +1 -1
- package/dist/esm/core/serviceToken.js +1 -1
- package/dist/esm/index.js +27 -0
- package/dist/esm/resources/billing.js +21 -0
- package/dist/esm/resources/checkoutSessions.js +2 -1
- package/dist/esm/resources/connectedAccounts.js +59 -0
- package/dist/esm/resources/merchants.js +46 -0
- package/dist/esm/resources/paymentIntents.js +28 -0
- package/dist/esm/resources/refunds.js +50 -0
- package/dist/esm/resources/transfers.js +45 -0
- package/dist/types/browser/mount.d.ts +23 -1
- package/dist/types/browser/payerClient.d.ts +56 -2
- package/dist/types/checkout.d.ts +2 -1
- package/dist/types/core/errors.d.ts +2 -2
- package/dist/types/index.d.ts +40 -1
- package/dist/types/resources/billing.d.ts +15 -0
- package/dist/types/resources/checkoutSessions.d.ts +15 -1
- package/dist/types/resources/connectedAccounts.d.ts +84 -0
- package/dist/types/resources/merchants.d.ts +113 -0
- package/dist/types/resources/paymentIntents.d.ts +44 -0
- package/dist/types/resources/refunds.d.ts +75 -0
- package/dist/types/resources/transfers.d.ts +73 -0
- 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
|
-
/**
|
|
10
|
-
|
|
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`. */
|
package/dist/types/checkout.d.ts
CHANGED
|
@@ -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 `@
|
|
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
|
-
* - `@
|
|
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>,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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 {
|
|
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.
|
|
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": "
|
|
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
|
},
|