Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ import {
import type { CompressionConfig } from "./compression.js";
import { calculateFee } from "./fee.js";
import { resolveToken } from "./token.js";
import { generatePaymentReceipt } from "./receipt.js";
import type { PaymentReceipt } from "./receipt.js";
import { checkInvoiceExpiry, checkPayerReadiness } from "./preflightChecker.js";
import { createInvoiceSubscription } from "./subscription.js";
import type { Subscription, InvoiceEvent, SubscriptionOptions } from "./types.js";
import { getSubscriptionManager } from "./streaming/SubscriptionManager.js";
Expand Down Expand Up @@ -2073,6 +2075,75 @@ export class StellarSplitClient extends TypedEventEmitter<SplitClientEventMap> {
* @throws WaterfallInsufficientFundsError if any tier is unsatisfied and
* partial submission wasn't allowed.
*/
/**
* Preflight checks before submitting a payment.
*
* Verifies the invoice is pending, not expired, and the payer has trustline/balance.
*/
async preflightCheck(params: {
invoiceId: string;
payer: string;
amount: bigint;
}): Promise<{
valid: boolean;
expiry: import("./preflightChecker.js").InvoiceExpiryResult;
payerReadiness: import("./preflightChecker.js").PayerReadinessResult;
}> {
const invoice = await this.getInvoice(params.invoiceId);
if (invoice.status !== "Pending") {
throw new InvoiceNotPendingError(params.invoiceId);
}

const expiry = checkInvoiceExpiry(Number(invoice.deadline), params.invoiceId);

// Call checkPayerReadiness, mapping the RPC server to a custom object that
// returns the account balances via getAccountBalances (Horizon).
const fakeServer = {
getAccount: async (address: string) => {
const normalizedBalances = await this.getAccountBalances(address);
const balances = normalizedBalances.map((nb) => {
if (nb.asset === "native") {
return {
balance: nb.balance,
asset_type: "native",
};
} else {
const [code, issuer] = nb.asset.split(":");
return {
balance: nb.balance,
asset_type: "credit_alphanum4",
asset_code: code,
asset_issuer: issuer,
};
}
});
return { balances };
},
} as any;

const payerReadiness = await checkPayerReadiness(
fakeServer,
params.payer,
params.amount,
invoice.token
);

const valid = expiry.valid && payerReadiness.ready;

return {
valid,
expiry,
payerReadiness,
};
}

/**
* Fetch a payment receipt for an invoice and payer.
*/
async getReceipt(invoiceId: string, payerAddress: string): Promise<PaymentReceipt> {
return generatePaymentReceipt(this, invoiceId, payerAddress);
}

async submitPayment(params: {
invoiceId: string;
payer: string;
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { StellarSplitClientConfig } from "./client.js";
import type { ExportFormat } from "./export.js";

export { StellarSplitClient } from "./client.js";
export { FinalityChecker } from "./finalityChecker.js";
export type {
StellarSplitClientConfig,
NetworkConfig,
Expand Down
15 changes: 14 additions & 1 deletion src/receipt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export interface PaymentReceipt {
decodedResult?: DecodedTransactionResult;
/** Convert receipt to a JSON-serializable object (with bigints represented as strings). */
toJSON(): PaymentReceiptJSON;
/** Payment status (optional). */
status?: "pending" | "finalized";
/** Balance deltas for recipients (optional). */
effectSummary?: Record<string, bigint>;
}

/** Options controlling optional receipt enrichment. */
Expand Down Expand Up @@ -228,7 +232,7 @@ function _buildReceiptObject(data: {
ledgerTimestamp: data.ledgerTimestamp,
decodedResult: data.decodedResult,
toJSON(): PaymentReceiptJSON {
return {
const json: PaymentReceiptJSON = {
invoiceId: this.invoiceId,
payer: this.payer,
totalPaid: this.totalPaid.toString(),
Expand All @@ -241,6 +245,15 @@ function _buildReceiptObject(data: {
ledgerTimestamp: this.ledgerTimestamp,
decodedResult: this.decodedResult,
};
if (this.status) {
json.status = this.status;
}
if (this.effectSummary) {
json.effectSummary = Object.fromEntries(
Object.entries(this.effectSummary).map(([k, v]) => [k, v.toString()])
);
}
return json;
},
};
}
Loading