@stacksjs/payments 0.75.342 → 0.75.343
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/README.md +10 -0
- package/dist/connect/financials.d.ts +30 -0
- package/dist/connect/financials.js +1 -0
- package/dist/connect/index.d.ts +3 -0
- package/dist/connect/index.js +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -61,3 +61,13 @@ Many thanks to the following core technologies & people who have contributed to
|
|
|
61
61
|
The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stacks/tree/main/LICENSE.md) for more information.
|
|
62
62
|
|
|
63
63
|
Made with 💙
|
|
64
|
+
|
|
65
|
+
## Direct-charge financial reconciliation
|
|
66
|
+
|
|
67
|
+
`directChargeFinancials(paymentIntentId, originalAccount)` reads canonical provider facts on the payment's original connected account. It returns the successful charge, every refund and dispute, its application fee and fee refunds, and their balance transactions. Platform fee reads stay on the platform. All lists are paginated, ownership is verified, and incomplete pagination fails. Importing the helper does not initialize Stripe.
|
|
68
|
+
|
|
69
|
+
Call after acquiring the application's payment lock. Persist immutable movement IDs with their account scope, and use the normal durable queue to retry `DirectChargeFinancialsPendingError`: fees and balance transactions can appear asynchronously. A missing fee is never inferred to be zero. Do not expose the returned Stripe objects in public responses.
|
|
70
|
+
|
|
71
|
+
Balance amounts, fees and net remain in their actual settlement currency with the provider exchange rate. Contributor allocations belong in their own presentment-currency ledger; a dispute status alone does not prove reinstatement, and a charge refund does not prove that the application fee was returned.
|
|
72
|
+
|
|
73
|
+
References: [direct charges](https://docs.stripe.com/connect/direct-charges), [refund pagination](https://docs.stripe.com/api/refunds/list), [disputes](https://docs.stripe.com/api/disputes/list), [application fees](https://docs.stripe.com/api/application_fees/list).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type Stripe from 'stripe';
|
|
2
|
+
/** Retrieve current facts on the ORIGINAL direct-charge account. Call after
|
|
3
|
+
* acquiring the application's payment lock, not before it. Every list is fully
|
|
4
|
+
* paginated; platform application fees never use the connected-account header.
|
|
5
|
+
* No writes, transfers, credentials, or event-snapshot state are accepted.
|
|
6
|
+
* https://docs.stripe.com/connect/direct-charges
|
|
7
|
+
*/
|
|
8
|
+
export declare function directChargeFinancials(paymentIntentId: string, account: string, client?: Stripe): Promise<DirectChargeFinancials>;
|
|
9
|
+
export declare interface DirectChargeMovement {
|
|
10
|
+
account: string | 'platform'
|
|
11
|
+
sourceId: string
|
|
12
|
+
kind: 'charge' | 'refund' | 'dispute' | 'application_fee' | 'application_fee_refund'
|
|
13
|
+
transaction: Stripe.BalanceTransaction
|
|
14
|
+
}
|
|
15
|
+
/** Server-only provider facts. Settlement amounts retain their own currency and
|
|
16
|
+
* exchange rate; they are not contributor allocations or available funds. */
|
|
17
|
+
export declare interface DirectChargeFinancials {
|
|
18
|
+
account: string
|
|
19
|
+
charge: Stripe.Charge
|
|
20
|
+
refunds: Stripe.Refund[]
|
|
21
|
+
disputes: Stripe.Dispute[]
|
|
22
|
+
applicationFee: Stripe.ApplicationFee | null
|
|
23
|
+
applicationFeeRefunds: Stripe.FeeRefund[]
|
|
24
|
+
movements: DirectChargeMovement[]
|
|
25
|
+
}
|
|
26
|
+
/** Stripe creates application fees/balance transactions asynchronously. Retry
|
|
27
|
+
* through the caller's durable queue; absence must never mean a zero fee. */
|
|
28
|
+
export declare class DirectChargeFinancialsPendingError extends Error {
|
|
29
|
+
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{stripe}from"../drivers/stripe";import{asAccount}from"./direct";export class DirectChargeFinancialsPendingError extends Error{}function id(value){return typeof value==="string"?value:String(value?.id||"")}async function allPages(read){const rows=[],seen=new Set;let after;for(let page=0;page<100;page++){const result=await read(after);for(const row of result.data){if(!row.id||seen.has(row.id))throw Error("Incomplete or repeated Stripe financial page");seen.add(row.id);rows.push(row)}if(!result.has_more)return rows;if(!result.data.length)throw Error("Incomplete Stripe financial page");after=result.data.at(-1).id}throw Error("Stripe financial pagination exceeded its bound")}export async function directChargeFinancials(paymentIntentId,account,client=stripe){const options=asAccount(account);if(!/^pi_[a-z0-9_]+$/i.test(paymentIntentId))throw Error("Expected a Stripe PaymentIntent ID");const intent=await client.paymentIntents.retrieve(paymentIntentId,{},options);if(intent.id!==paymentIntentId||intent.status!=="succeeded"||!id(intent.latest_charge))throw new DirectChargeFinancialsPendingError("The original payment has no successful charge yet");const charge=await client.charges.retrieve(id(intent.latest_charge),{},options);if(id(charge.payment_intent)!==paymentIntentId||charge.id!==id(intent.latest_charge)||!charge.paid||!charge.captured)throw Error("Charge does not match the successful direct payment");const refunds=await allPages((after)=>client.refunds.list({charge:charge.id,limit:100,...after?{starting_after:after}:{}},options)),disputes=await allPages((after)=>client.disputes.list({charge:charge.id,limit:100,...after?{starting_after:after}:{}},options));for(const item of[...refunds,...disputes])if(id(item.charge)!==charge.id||item.payment_intent&&id(item.payment_intent)!==paymentIntentId)throw Error("Foreign refund or dispute in direct-charge financials");let applicationFee=null;if(id(charge.application_fee))applicationFee=await client.applicationFees.retrieve(id(charge.application_fee));else if(Number(charge.application_fee_amount)>0){const fees=await allPages((after)=>client.applicationFees.list({charge:charge.id,limit:100,...after?{starting_after:after}:{}}));if(fees.length!==1)throw new DirectChargeFinancialsPendingError("The direct-charge application fee is not available yet");applicationFee=fees[0]}if(applicationFee&&(id(applicationFee.account)!==account||id(applicationFee.charge)!==charge.id))throw Error("Application fee belongs to another direct charge or account");const applicationFeeRefunds=applicationFee?await allPages((after)=>client.applicationFees.listRefunds(applicationFee.id,{limit:100,...after?{starting_after:after}:{}})):[];if(applicationFeeRefunds.some((refund)=>id(refund.fee)!==applicationFee.id))throw Error("Foreign application-fee refund");const movements=[],seen=new Map;async function movement(reference,sourceId,kind,scope,required=!0){const transactionId=id(reference);if(!transactionId){if(required)throw new DirectChargeFinancialsPendingError("A provider balance transaction is not available yet");return}if(!/^txn_[a-z0-9_]+$/i.test(transactionId))throw Error("Invalid provider balance transaction");const transaction=await client.balanceTransactions.retrieve(transactionId,{},scope==="platform"?void 0:options);if(transaction.id!==transactionId||id(transaction.source)&&id(transaction.source)!==sourceId)throw Error("Foreign provider balance transaction");const entry={account:scope,sourceId,kind,transaction},key=`${scope}:${transactionId}`;if(seen.has(key)){if(JSON.stringify(seen.get(key))!==JSON.stringify(entry))throw Error("Conflicting provider balance transaction");return}seen.set(key,entry);movements.push(entry)}await movement(charge.balance_transaction,charge.id,"charge",account);for(const refund of refunds){await movement(refund.balance_transaction,refund.id,"refund",account,refund.status==="succeeded");await movement(refund.failure_balance_transaction,refund.id,"refund",account,!1)}for(const dispute of disputes)for(const transaction of dispute.balance_transactions)await movement(transaction,dispute.id,"dispute",account);if(applicationFee)await movement(applicationFee.balance_transaction,applicationFee.id,"application_fee","platform");for(const refund of applicationFeeRefunds)await movement(refund.balance_transaction,refund.id,"application_fee_refund","platform");return{account,charge,refunds,disputes,applicationFee,applicationFeeRefunds,movements}}
|
package/dist/connect/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { asAccount, connectedCustomer, directCheckout, directCheckoutPaymentData, directCheckoutSubscriptionData, directPaymentIntentParams, directSubscriptionParams, manageDirectSubscription } from './direct';
|
|
2
2
|
import { checkoutLineItemsTotal, destinationCheckoutPaymentData, destinationCheckoutSubscriptionData, destinationFeeCents, destinationFeePercent, destinationPaymentIntentParams, destinationSubscriptionParams, splitFee } from './destination';
|
|
3
3
|
import { connectRefund, connectRefundParams, isDestinationCharge } from './refund';
|
|
4
|
+
import { directChargeFinancials } from './financials';
|
|
4
5
|
import { manageConnectedAccount } from './account';
|
|
5
6
|
import { manageConnectWebhook } from './webhook';
|
|
6
7
|
import { manageDestinationCharge } from './charge';
|
|
@@ -29,6 +30,7 @@ export declare const connect: {
|
|
|
29
30
|
asAccount: typeof asAccount;
|
|
30
31
|
customer: typeof connectedCustomer;
|
|
31
32
|
directCheckout: typeof directCheckout;
|
|
33
|
+
directChargeFinancials: typeof directChargeFinancials;
|
|
32
34
|
directPaymentIntentParams: typeof directPaymentIntentParams;
|
|
33
35
|
directCheckoutPaymentData: typeof directCheckoutPaymentData;
|
|
34
36
|
directCheckoutSubscriptionData: typeof directCheckoutSubscriptionData;
|
|
@@ -74,6 +76,7 @@ export * from './account';
|
|
|
74
76
|
export * from './charge';
|
|
75
77
|
export * from './destination';
|
|
76
78
|
export * from './direct';
|
|
79
|
+
export * from './financials';
|
|
77
80
|
export * from './payout';
|
|
78
81
|
export * from './refund';
|
|
79
82
|
export * from './subscription';
|
package/dist/connect/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./account";export*from"./charge";export*from"./destination";export*from"./direct";export*from"./payout";export*from"./refund";export*from"./subscription";export*from"./transfer";export*from"./webhook";import{manageConnectedAccount}from"./account";import{manageDestinationCharge}from"./charge";import{asAccount,connectedCustomer,directCheckout,directCheckoutPaymentData,directCheckoutSubscriptionData,directPaymentIntentParams,directSubscriptionParams,manageDirectSubscription}from"./direct";import{checkoutLineItemsTotal,destinationCheckoutPaymentData,destinationCheckoutSubscriptionData,destinationFeeCents,destinationFeePercent,destinationPaymentIntentParams,destinationSubscriptionParams,splitFee}from"./destination";import{managePayout}from"./payout";import{connectRefund,connectRefundParams,isDestinationCharge}from"./refund";import{manageDestinationSubscription}from"./subscription";import{manageTransfer}from"./transfer";import{manageConnectWebhook}from"./webhook";export const connect={createAccount:manageConnectedAccount.createConnectedAccount,createTestAccount:manageConnectedAccount.createTestConnectedAccount,onboardingLink:manageConnectedAccount.accountOnboardingLink,accountStatus:manageConnectedAccount.accountStatus,retrieveAccount:manageConnectedAccount.retrieveConnectedAccount,destinationCharge:manageDestinationCharge.destinationCharge,applicationFeeFor:manageDestinationCharge.applicationFeeFor,feeCents:destinationFeeCents,feePercent:destinationFeePercent,paymentIntentParams:destinationPaymentIntentParams,checkoutPaymentData:destinationCheckoutPaymentData,checkoutSubscriptionData:destinationCheckoutSubscriptionData,subscriptionParams:destinationSubscriptionParams,lineItemsTotal:checkoutLineItemsTotal,splitFee,asAccount,customer:connectedCustomer,directCheckout,directPaymentIntentParams,directCheckoutPaymentData,directCheckoutSubscriptionData,directSubscriptionParams,directSubscriptionCheckout:manageDirectSubscription.checkout,refund:connectRefund,refundParams:connectRefundParams,isDestinationCharge,subscriptionCheckout:manageDestinationSubscription.checkout,subscriptionFeePercent:manageDestinationSubscription.feePercentFor,subscriptionDestination:manageDestinationSubscription.destinationOf,createTransfer:manageTransfer.createTransfer,reverseTransfer:manageTransfer.reverseTransfer,listTransfers:manageTransfer.listTransfers,retrieveTransfer:manageTransfer.retrieveTransfer,listPayouts:managePayout.listPayouts,retrievePayout:managePayout.retrievePayout,accountBalance:managePayout.accountBalance,onAccount:manageConnectWebhook.onAccount,onPayout:manageConnectWebhook.onPayout,getAccount:manageConnectWebhook.getAccount,getPayout:manageConnectWebhook.getPayout,getConnectedAccountId:manageConnectWebhook.getConnectedAccountId,account:manageConnectedAccount,charge:manageDestinationCharge,subscription:manageDestinationSubscription,directSubscription:manageDirectSubscription,transfer:manageTransfer,payout:managePayout};
|
|
1
|
+
export*from"./account";export*from"./charge";export*from"./destination";export*from"./direct";export*from"./financials";export*from"./payout";export*from"./refund";export*from"./subscription";export*from"./transfer";export*from"./webhook";import{manageConnectedAccount}from"./account";import{manageDestinationCharge}from"./charge";import{asAccount,connectedCustomer,directCheckout,directCheckoutPaymentData,directCheckoutSubscriptionData,directPaymentIntentParams,directSubscriptionParams,manageDirectSubscription}from"./direct";import{checkoutLineItemsTotal,destinationCheckoutPaymentData,destinationCheckoutSubscriptionData,destinationFeeCents,destinationFeePercent,destinationPaymentIntentParams,destinationSubscriptionParams,splitFee}from"./destination";import{managePayout}from"./payout";import{directChargeFinancials}from"./financials";import{connectRefund,connectRefundParams,isDestinationCharge}from"./refund";import{manageDestinationSubscription}from"./subscription";import{manageTransfer}from"./transfer";import{manageConnectWebhook}from"./webhook";export const connect={createAccount:manageConnectedAccount.createConnectedAccount,createTestAccount:manageConnectedAccount.createTestConnectedAccount,onboardingLink:manageConnectedAccount.accountOnboardingLink,accountStatus:manageConnectedAccount.accountStatus,retrieveAccount:manageConnectedAccount.retrieveConnectedAccount,destinationCharge:manageDestinationCharge.destinationCharge,applicationFeeFor:manageDestinationCharge.applicationFeeFor,feeCents:destinationFeeCents,feePercent:destinationFeePercent,paymentIntentParams:destinationPaymentIntentParams,checkoutPaymentData:destinationCheckoutPaymentData,checkoutSubscriptionData:destinationCheckoutSubscriptionData,subscriptionParams:destinationSubscriptionParams,lineItemsTotal:checkoutLineItemsTotal,splitFee,asAccount,customer:connectedCustomer,directCheckout,directChargeFinancials,directPaymentIntentParams,directCheckoutPaymentData,directCheckoutSubscriptionData,directSubscriptionParams,directSubscriptionCheckout:manageDirectSubscription.checkout,refund:connectRefund,refundParams:connectRefundParams,isDestinationCharge,subscriptionCheckout:manageDestinationSubscription.checkout,subscriptionFeePercent:manageDestinationSubscription.feePercentFor,subscriptionDestination:manageDestinationSubscription.destinationOf,createTransfer:manageTransfer.createTransfer,reverseTransfer:manageTransfer.reverseTransfer,listTransfers:manageTransfer.listTransfers,retrieveTransfer:manageTransfer.retrieveTransfer,listPayouts:managePayout.listPayouts,retrievePayout:managePayout.retrievePayout,accountBalance:managePayout.accountBalance,onAccount:manageConnectWebhook.onAccount,onPayout:manageConnectWebhook.onPayout,getAccount:manageConnectWebhook.getAccount,getPayout:manageConnectWebhook.getPayout,getConnectedAccountId:manageConnectWebhook.getConnectedAccountId,account:manageConnectedAccount,charge:manageDestinationCharge,subscription:manageDestinationSubscription,directSubscription:manageDirectSubscription,transfer:manageTransfer,payout:managePayout};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/payments",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.343",
|
|
6
6
|
"description": "The Stacks payments package. Currently supporting Stripe.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"prepublishOnly": "bun run build"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@stacksjs/config": "0.75.
|
|
64
|
-
"@stacksjs/database": "0.75.
|
|
65
|
-
"@stacksjs/error-handling": "0.75.
|
|
66
|
-
"@stacksjs/logging": "0.75.
|
|
67
|
-
"@stacksjs/orm": "0.75.
|
|
68
|
-
"@stacksjs/types": "0.75.
|
|
63
|
+
"@stacksjs/config": "0.75.343",
|
|
64
|
+
"@stacksjs/database": "0.75.343",
|
|
65
|
+
"@stacksjs/error-handling": "0.75.343",
|
|
66
|
+
"@stacksjs/logging": "0.75.343",
|
|
67
|
+
"@stacksjs/orm": "0.75.343",
|
|
68
|
+
"@stacksjs/types": "0.75.343"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@stripe/stripe-js": "^10.0.0",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@stacksjs/utils": "0.75.
|
|
83
|
+
"@stacksjs/utils": "0.75.343",
|
|
84
84
|
"@stripe/stripe-js": "^10.0.0",
|
|
85
85
|
"better-dx": "^0.2.24",
|
|
86
86
|
"stripe": "23.0.0"
|