@profullstack/coinpay 0.22.0 → 0.24.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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to `@profullstack/coinpay` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.23.0] - 2026-10-21
9
+
10
+ ### Added
11
+ - `createPayment({ successUrl, cancelUrl })`, like Stripe Checkout: once the payment confirms, the
12
+ hosted pay page sends the customer to `successUrl` (`{PAYMENT_ID}` is replaced with the payment id);
13
+ `cancelUrl` is the "Back to merchant" link from an expired checkout. Before this the crypto checkout
14
+ had no way to return the buyer and left them on "Payment Complete!".
15
+
8
16
  ## [0.22.0] - 2026-10-19
9
17
 
10
18
  ### Added
package/bin/coinpay.js CHANGED
@@ -710,6 +710,9 @@ ${colors.cyan}Commands:${colors.reset}
710
710
  books summary Totals by tax category (--period 2026|2026-Q3|2026-08, --scope)
711
711
  books export CPA pack (--period, --scope, --format csv|pdf|html|json, --output;
712
712
  --with-documents: a ZIP with the period's tax documents, up to 8 MiB)
713
+ pnl Profit and loss, personal and business as separate files
714
+ (--scope personal|business, --months 3 (default: last 3
715
+ complete months) or --from/--to, --name "Holder", --format pdf|html|csv|json)
713
716
  payloads [list|download <id>] Raw provider responses, exactly as received
714
717
  reports send <id> Email a report (--to a@x,b@y, --format pdf,csv, --message, --no-attach)
715
718
  books send Email the CPA pack (--to, --period, --scope, --format,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/coinpay",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "CoinPay SDK & CLI \u2014 Accept cryptocurrency and card payments (BTC, ETH, SOL, POL, BCH, USDC) with wallet, swap and a live finances dashboard",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/client.d.ts CHANGED
@@ -27,6 +27,10 @@ export interface PaymentParams {
27
27
  description?: string;
28
28
  /** Custom metadata attached to the payment (e.g., `{ orderId: 'ORD-123' }`) */
29
29
  metadata?: Record<string, unknown>;
30
+ /** Where the pay page sends the customer once the payment confirms, like Stripe's `success_url`. `{PAYMENT_ID}` is replaced with the payment id. */
31
+ successUrl?: string;
32
+ /** Where "Back to merchant" goes from an expired or abandoned checkout */
33
+ cancelUrl?: string;
30
34
  }
31
35
 
32
36
  /** Parameters for listing payments */
package/src/client.js CHANGED
@@ -228,6 +228,8 @@ export class CoinPayClient {
228
228
  * @param {string} params.blockchain - Blockchain/cryptocurrency (BTC, ETH, SOL, POL, BCH, USDC_ETH, USDC_POL, USDC_SOL)
229
229
  * @param {string} [params.description] - Payment description (shown to customer)
230
230
  * @param {Object} [params.metadata] - Custom metadata (e.g., { orderId: 'ORD-123', customerId: 'CUST-456' })
231
+ * @param {string} [params.successUrl] - Where the pay page sends the customer once the payment confirms, like Stripe's success_url. {PAYMENT_ID} is replaced with the payment id.
232
+ * @param {string} [params.cancelUrl] - Where "Back to merchant" goes from an expired or abandoned checkout
231
233
  * @returns {Promise<Object>} Created payment with address and QR code
232
234
  *
233
235
  * @example
@@ -238,7 +240,8 @@ export class CoinPayClient {
238
240
  * currency: 'USD',
239
241
  * blockchain: 'BTC',
240
242
  * description: 'Order #12345',
241
- * metadata: { orderId: '12345', customerEmail: 'customer@example.com' }
243
+ * metadata: { orderId: '12345', customerEmail: 'customer@example.com' },
244
+ * successUrl: 'https://yoursite.com/orders/12345?payment={PAYMENT_ID}',
242
245
  * });
243
246
  *
244
247
  * // Display payment.payment_address and payment.qr_code to customer
@@ -250,6 +253,8 @@ export class CoinPayClient {
250
253
  blockchain,
251
254
  description,
252
255
  metadata,
256
+ successUrl,
257
+ cancelUrl,
253
258
  }) {
254
259
  // Map to API field names (snake_case)
255
260
  return this.request('/payments/create', {
@@ -261,6 +266,8 @@ export class CoinPayClient {
261
266
  blockchain: blockchain?.toUpperCase(),
262
267
  description,
263
268
  metadata,
269
+ success_url: successUrl,
270
+ cancel_url: cancelUrl,
264
271
  }),
265
272
  });
266
273
  }
@@ -802,6 +802,30 @@ export async function runFinancesCommand(subcommand, args, flags, ctx) {
802
802
  throw new CliExit(EXIT.INVALID, 'Usage: coinpay finances books [queue|confirm <id>|confirm-all --yes|categorize|rules [add|delete]|summary|export|send]');
803
803
  }
804
804
 
805
+ case 'pnl':
806
+ case 'profit-loss': {
807
+ const scopes = listFlag(flags.scope);
808
+ const wanted = scopes.length ? scopes : ['personal', 'business'];
809
+ for (const s of wanted) if (s !== 'personal' && s !== 'business') throw new CliExit(EXIT.INVALID, `--scope must be personal, business or both (got ${s})`);
810
+ const format = typeof flags.format === 'string' ? flags.format.toLowerCase() : 'pdf';
811
+ if (!['pdf', 'html', 'csv', 'json'].includes(format)) throw new CliExit(EXIT.INVALID, '--format must be pdf, html, csv or json');
812
+ const months = flags.months === undefined ? undefined : Number(flags.months);
813
+ if (months !== undefined && (!Number.isInteger(months) || months < 1 || months > 24)) throw new CliExit(EXIT.INVALID, '--months must be 1 to 24');
814
+ const period = typeof flags.period === 'string' ? flags.period : undefined;
815
+ const from = typeof flags.from === 'string' ? flags.from : undefined;
816
+ const to = typeof flags.to === 'string' ? flags.to : undefined;
817
+ const timezone = typeof flags.timezone === 'string' ? flags.timezone : undefined;
818
+ const name = typeof flags.name === 'string' ? flags.name : undefined;
819
+ const written = [];
820
+ for (const scope of wanted) {
821
+ const file = await api.exportPnl(client, { scope, months, period, from, to, timezone, name, format });
822
+ const output = typeof flags.output === 'string' && wanted.length === 1 ? flags.output : file.filename || `pnl-${scope}.${format}`;
823
+ written.push({ scope, output: writeDownload(output, Buffer.from(file.bytes), { overwrite: flags.overwrite === true }), bytes: file.bytes.length });
824
+ }
825
+ emit({ files: written }, written.map((w) => `Wrote ${w.output} (${w.scope}, ${w.bytes} bytes)`).join('\n'));
826
+ return EXIT.OK;
827
+ }
828
+
805
829
  case 'digest': {
806
830
  const action = args[0] || 'show';
807
831
  const DAY_NAMES = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
@@ -966,4 +990,4 @@ export async function runFinancesCommand(subcommand, args, flags, ctx) {
966
990
  }
967
991
  }
968
992
 
969
- export const EXTENDED_SUBCOMMANDS = ['connect', 'disconnect', 'consent', 'backfill', 'jobs', 'coverage', 'report', 'reports', 'statements', 'books', 'payloads', 'digest', 'due', 'due-soon', 'recurring', 'due-day', 'daily'];
993
+ export const EXTENDED_SUBCOMMANDS = ['connect', 'disconnect', 'consent', 'backfill', 'jobs', 'coverage', 'report', 'reports', 'statements', 'books', 'pnl', 'profit-loss', 'payloads', 'digest', 'due', 'due-soon', 'recurring', 'due-day', 'daily'];
@@ -397,6 +397,16 @@ export async function exportBooks(client, { period, from, to, timezone, scope, f
397
397
  return client.requestBinary(`/finances/books/export${query({ period, from, to, timezone, scope, format, with_documents: withDocuments ? 1 : undefined })}`);
398
398
  }
399
399
 
400
+ // ── Profit and loss ──
401
+
402
+ /**
403
+ * Profit and loss statement for one scope. With no period it covers the
404
+ * last `months` complete calendar months (server default 3).
405
+ */
406
+ export async function exportPnl(client, { scope, months, period, from, to, timezone, name, format = 'pdf' } = {}) {
407
+ return client.requestBinary(`/finances/pnl${query({ scope, months, period, from, to, timezone, name, format })}`);
408
+ }
409
+
400
410
  // ── Raw provider payloads ──
401
411
 
402
412
  export async function listFinancePayloads(client, { connectionId, limit, offset } = {}) {
package/src/payments.d.ts CHANGED
@@ -25,6 +25,10 @@ export interface CreatePaymentParams {
25
25
  description?: string;
26
26
  /** Custom metadata for your records */
27
27
  metadata?: Record<string, unknown>;
28
+ /** Where the pay page sends the customer once the payment confirms, like Stripe's `success_url`. `{PAYMENT_ID}` is replaced with the payment id. */
29
+ successUrl?: string;
30
+ /** Where "Back to merchant" goes from an expired or abandoned checkout */
31
+ cancelUrl?: string;
28
32
  }
29
33
 
30
34
  /** Parameters for the standalone `getPayment` function */
package/src/payments.js CHANGED
@@ -34,6 +34,8 @@ import { CoinPayClient } from './client.js';
34
34
  * @param {string} params.blockchain - Blockchain to use (BTC, ETH, SOL, POL, BCH, USDC_ETH, USDC_POL, USDC_SOL)
35
35
  * @param {string} [params.description] - Payment description shown to customer
36
36
  * @param {Object} [params.metadata] - Custom metadata for your records
37
+ * @param {string} [params.successUrl] - Where the pay page sends the customer after the payment confirms ({PAYMENT_ID} is filled in)
38
+ * @param {string} [params.cancelUrl] - Where "Back to merchant" goes from an expired checkout
37
39
  * @returns {Promise<Object>} Created payment with address and QR code
38
40
  *
39
41
  * @example
@@ -60,6 +62,8 @@ export async function createPayment({
60
62
  blockchain,
61
63
  description,
62
64
  metadata,
65
+ successUrl,
66
+ cancelUrl,
63
67
  }) {
64
68
  const coinpay = client || new CoinPayClient({ apiKey });
65
69
 
@@ -70,6 +74,8 @@ export async function createPayment({
70
74
  blockchain,
71
75
  description,
72
76
  metadata,
77
+ successUrl,
78
+ cancelUrl,
73
79
  });
74
80
  }
75
81