payment-kit 1.29.12 → 1.29.13

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.
@@ -1243,7 +1243,18 @@ export async function startCheckoutSessionFromPaymentLink(id: string, c: any) {
1243
1243
  const body = c.get('sanitizedBody') ?? {};
1244
1244
  const { metadata, needShortUrl = false } = body;
1245
1245
  try {
1246
- const link = await PaymentLink.findByPk(id);
1246
+ let link = await PaymentLink.findByPk(id);
1247
+ // Preview-draft ids are `plink_<did>` (create page). The frontend builds them from the
1248
+ // BARE login_token did (e.g. z1cxgj7…), but /stash persisted the link with the
1249
+ // auth-normalized `did:abt:` prefix (or vice versa) — a bare/prefixed DID shape
1250
+ // mismatch. Fall back to the other shape so the preview iframe resolves its own draft.
1251
+ if (!link && id.startsWith('plink_')) {
1252
+ const didPart = id.slice('plink_'.length);
1253
+ const altId = didPart.startsWith('did:abt:')
1254
+ ? `plink_${didPart.slice('did:abt:'.length)}`
1255
+ : `plink_did:abt:${didPart}`;
1256
+ link = await PaymentLink.findByPk(altId);
1257
+ }
1247
1258
  if (!link) {
1248
1259
  return c.json({ error: 'Payment link not found, please contact the source of the payment link.' }, 400);
1249
1260
  }
@@ -77,10 +77,13 @@ app.get('/', auth, async (c) => {
77
77
  attributes: {
78
78
  include: [
79
79
  [
80
+ // Sequelize aliases the primary table to its modelName (`TaxRate`)
81
+ // in findAndCountAll, so the bare table name `tax_rates.id` is not a
82
+ // valid column in the emitted SQL (#1395). Reference the alias.
80
83
  literal(`(
81
84
  SELECT COUNT(DISTINCT ii.invoice_id)
82
85
  FROM invoice_items AS ii
83
- WHERE ii.tax_rate_id = tax_rates.id
86
+ WHERE ii.tax_rate_id = \`TaxRate\`.id
84
87
  )`),
85
88
  'invoice_count',
86
89
  ],
@@ -0,0 +1,48 @@
1
+ // Regression test for #1395 — GET /api/tax-rates returned 400
2
+ // `SQLITE_ERROR: no such column: tax_rates.id`.
3
+ //
4
+ // The list handler's `invoice_count` subquery used `literal()` with the bare
5
+ // table name `tax_rates.id`, but Sequelize aliases the primary table to its
6
+ // modelName (`TaxRate`) in findAndCountAll, so that qualifier doesn't exist in
7
+ // the emitted SQL. The error is in the compiled subquery itself, so it throws
8
+ // even on an empty table — which is exactly what makes a tiny DB-backed test
9
+ // (real sqlite, like store/tenant-model.spec.ts) enough to pin it.
10
+ //
11
+ // auth is stubbed to pass-through (gating is covered by
12
+ // middlewares/hono/security.spec.ts).
13
+ jest.mock('../../../src/middlewares/hono/security', () => ({
14
+ authenticate: () => (_c: any, next: any) => next(),
15
+ }));
16
+
17
+ import { Hono } from 'hono';
18
+ import { Sequelize } from 'sequelize';
19
+
20
+ import { withTenant } from '../../../src/libs/context';
21
+ import { initialize } from '../../../src/store/models';
22
+ import { TENANT_A } from '../../fixtures/tenants';
23
+ import taxRates from '../../../src/routes/hono/tax-rates';
24
+
25
+ const sequelize = new Sequelize('sqlite::memory:', { logging: false });
26
+ initialize(sequelize);
27
+
28
+ const app = new Hono();
29
+ app.route('/api/tax-rates', taxRates);
30
+
31
+ beforeAll(async () => {
32
+ await sequelize.sync({ force: true });
33
+ });
34
+ afterAll(() => sequelize.close());
35
+
36
+ describe('GET /api/tax-rates — list (regression #1395)', () => {
37
+ it('the invoice_count subquery alias resolves — 200, not 400 no-such-column', async () => {
38
+ const r = await withTenant(TENANT_A, () =>
39
+ app.fetch(new Request('http://app.local/api/tax-rates', { headers: { host: 'app.local' } })),
40
+ );
41
+
42
+ // pre-fix: 400 { error: "SQLITE_ERROR: no such column: tax_rates.id" }
43
+ expect(r.status).toBe(200);
44
+ const body = (await r.json()) as { count: number; list: unknown[] };
45
+ expect(body.count).toBe(0);
46
+ expect(Array.isArray(body.list)).toBe(true);
47
+ });
48
+ });
package/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.29.12
17
+ version: 1.29.13
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.29.12",
3
+ "version": "1.29.13",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "prelint": "npm run types",
@@ -60,9 +60,9 @@
60
60
  "@blocklet/error": "^0.3.5",
61
61
  "@blocklet/js-sdk": "^1.17.13-beta-20260613-094425-b81920c8",
62
62
  "@blocklet/logger": "^1.17.13-beta-20260613-094425-b81920c8",
63
- "@blocklet/payment-broker-client": "1.29.12",
64
- "@blocklet/payment-react": "1.29.12",
65
- "@blocklet/payment-vendor": "1.29.12",
63
+ "@blocklet/payment-broker-client": "1.29.13",
64
+ "@blocklet/payment-react": "1.29.13",
65
+ "@blocklet/payment-vendor": "1.29.13",
66
66
  "@blocklet/sdk": "^1.17.13-beta-20260613-094425-b81920c8",
67
67
  "@blocklet/ui-react": "^3.5.4",
68
68
  "@blocklet/uploader": "^0.15.4",
@@ -133,7 +133,7 @@
133
133
  "devDependencies": {
134
134
  "@abtnode/types": "^1.17.13-beta-20260613-094425-b81920c8",
135
135
  "@arcblock/eslint-config-ts": "^0.3.3",
136
- "@blocklet/payment-types": "1.29.12",
136
+ "@blocklet/payment-types": "1.29.13",
137
137
  "@types/connect": "^3.4.38",
138
138
  "@types/debug": "^4.1.12",
139
139
  "@types/dotenv-flow": "^3.3.3",
@@ -180,5 +180,5 @@
180
180
  "parser": "typescript"
181
181
  }
182
182
  },
183
- "gitHead": "8473f4c5a7716d03d53f253563229f31e63ec0c4"
183
+ "gitHead": "e023f83ecfb07c0782e138a2802b5dec746fd7c6"
184
184
  }
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useImperativeHandle, useRef, useState } from 'react';
2
2
  import { useFullscreen, useSize } from 'ahooks';
3
+ import { joinURL } from 'ufo';
3
4
  import IframeResizer from 'iframe-resizer-react';
4
5
  import { useTheme } from '@mui/material';
5
6
  import Chrome from './chrome';
@@ -30,7 +31,11 @@ export default function PaymentLinkPreview({
30
31
  }
31
32
  }, [theme.palette.mode, mode]);
32
33
 
33
- const iframeSrc = `${window.blocklet.prefix}checkout/pay/${id}?preview=1&version=${version}&theme=${mode}`;
34
+ // joinURL (not string concat) so the payment prefix joins the path with exactly one
35
+ // slash. window.blocklet.prefix has NO trailing slash on embedded hosts
36
+ // (/.well-known/payment), so `${prefix}checkout/...` produced `/.well-known/paymentcheckout/...`
37
+ // → 404. Every other payment-link URL already uses joinURL; this was the lone hard-concat.
38
+ const iframeSrc = `${joinURL(window.blocklet.prefix, 'checkout/pay', id)}?preview=1&version=${version}&theme=${mode}`;
34
39
  return (
35
40
  <div ref={innerRef}>
36
41
  {fullscreen ? (