@aquarian-metals/coin-moebius-square 1.0.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/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +79 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +326 -0
- package/dist/server.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aquarian-metals
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @aquarian-metals/coin-moebius-square
|
|
2
|
+
|
|
3
|
+
Square (Block) provider for **[Coin Moebius](https://github.com/aquarian-metals/coin-moebius)**.
|
|
4
|
+
|
|
5
|
+
Two entries in one package:
|
|
6
|
+
|
|
7
|
+
- `@aquarian-metals/coin-moebius-square` — browser entry, redirects to Square's hosted checkout (Payment Link flow).
|
|
8
|
+
- `@aquarian-metals/coin-moebius-square/server` — Node-only HMAC-SHA256 webhook verifier. **Never import this from browser code.**
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @aquarian-metals/coin-moebius-square
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
No additional dependencies — the server verifier uses Web Crypto exclusively.
|
|
17
|
+
|
|
18
|
+
## Use — browser
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createSquareProvider } from '@aquarian-metals/coin-moebius-square';
|
|
22
|
+
import { createPaymentManager } from '@aquarian-metals/coin-moebius';
|
|
23
|
+
|
|
24
|
+
const payments = createPaymentManager({
|
|
25
|
+
providers: [
|
|
26
|
+
createSquareProvider({
|
|
27
|
+
sessionEndpoint: '/api/checkout/square',
|
|
28
|
+
}),
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Your session endpoint calls `POST /v2/online-checkout/payment-links` and returns the `payment_link.url` field as `{ url }`. The provider fires `onPending` and redirects the buyer to Square's hosted checkout.
|
|
34
|
+
|
|
35
|
+
## Use — server (webhook verification)
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { createSquareVerifier } from '@aquarian-metals/coin-moebius-square/server';
|
|
39
|
+
|
|
40
|
+
const verify = createSquareVerifier({
|
|
41
|
+
signatureKey: process.env.SQUARE_WEBHOOK_SIGNATURE_KEY,
|
|
42
|
+
notificationUrl: 'https://your-public-domain.example/webhooks/square',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// inside your webhook route:
|
|
46
|
+
const result = await verify.verify(rawBody, request.headers);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Why the verifier needs a `notificationUrl`
|
|
50
|
+
|
|
51
|
+
Square signs the webhook over **`notificationUrl + rawBody`** — the public URL the merchant configured on the webhook subscription is part of the HMAC input. A worker running behind a reverse proxy or Cloudflare typically cannot recover the original public URL from the inbound request (it sees an internal route, a different host, or a stripped path), so the verifier requires the merchant to pass the URL explicitly.
|
|
52
|
+
|
|
53
|
+
**The URL must match byte-for-byte** with what the merchant configured in Square's Developer Console (Webhooks → your subscription → Notification URL). Any difference — trailing slash, scheme, port, capitalization of the host — produces a silent signature failure. Treat the value like a secret and pin it in your environment configuration.
|
|
54
|
+
|
|
55
|
+
### Signature key
|
|
56
|
+
|
|
57
|
+
The signature key is **subscription-specific**, generated in Square's Developer Console alongside the subscription. It is not the same as the application's access token. To rotate, recreate the subscription.
|
|
58
|
+
|
|
59
|
+
### Status mapping
|
|
60
|
+
|
|
61
|
+
| Square event | Inner status | `PaymentResult.status` |
|
|
62
|
+
| ----------------------- | ---------------------- | ---------------------------------------------------- |
|
|
63
|
+
| `payment.created` | — | `pending` |
|
|
64
|
+
| `payment.updated` | `COMPLETED` | `success` |
|
|
65
|
+
| `payment.updated` | `APPROVED` (auth-only) | `pending` |
|
|
66
|
+
| `payment.updated` | `FAILED` or `CANCELED` | `failed` |
|
|
67
|
+
| `refund.created` | — | `pending` |
|
|
68
|
+
| `refund.updated` | `COMPLETED` | `refunded` |
|
|
69
|
+
| `refund.updated` | `FAILED` or `REJECTED` | `failed` |
|
|
70
|
+
| `dispute.created` | — | `disputed` |
|
|
71
|
+
| `dispute.state.updated` | — | `disputed` |
|
|
72
|
+
| anything else | — | (verifier returns `null`, signature still validated) |
|
|
73
|
+
|
|
74
|
+
The `paymentId` on the returned `PaymentResult` is the Square `payment.id` whenever available (preferred for cross-event correlation across payment / refund / dispute on the same purchase). Amounts are converted from Square's smallest-currency-unit integers (e.g., cents for USD) to a major-unit decimal to match the rest of the SDK.
|
|
75
|
+
|
|
76
|
+
### Known gap: no replay-window enforcement
|
|
77
|
+
|
|
78
|
+
Square's signature scheme does not include a timestamp, so the verifier cannot reject stale-and-replayed deliveries on its own. If your application is sensitive to replays, deduplicate at the application layer using the webhook's `event_id` field.
|
|
79
|
+
|
|
80
|
+
### Sandbox vs production
|
|
81
|
+
|
|
82
|
+
The signature scheme is the same in both environments — the verifier has no mode flag. Sandbox API endpoints are at `connect.squareupsandbox.com`; production is at `connect.squareup.com`. Your session endpoint chooses which one to hit; the verifier only cares about the signature key + notification URL pair.
|
|
83
|
+
|
|
84
|
+
### Currency + locations
|
|
85
|
+
|
|
86
|
+
Square locations have a per-location currency, but the webhook payload carries the actual currency on `amount_money.currency`, so the verifier surfaces whatever the payload says. Merchants with multiple locations don't need to override anything.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Square client-side provider for Coin Moebius.
|
|
3
|
+
*
|
|
4
|
+
* Hosted-checkout flow via Square's Payment Link API. The provider POSTs the
|
|
5
|
+
* buyer's selection to the caller's `sessionEndpoint`, receives `{ url }`
|
|
6
|
+
* pointing at the hosted checkout (`https://square.link/u/<id>` or the
|
|
7
|
+
* longer `https://checkout.square.site/<id>` form), fires `onPending`, and
|
|
8
|
+
* redirects the buyer. Square handles the wallet, funding source, and
|
|
9
|
+
* receipt; the merchant's server receives the webhook on completion.
|
|
10
|
+
*
|
|
11
|
+
* import { createSquareProvider } from '@aquarian-metals/coin-moebius-square';
|
|
12
|
+
* const square = createSquareProvider({
|
|
13
|
+
* sessionEndpoint: '/api/checkout/square',
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* const manager = createPaymentManager({ providers: [square] });
|
|
17
|
+
* await manager.initiate({ productId: 'pro', amount: 9.99, currency: 'USD' });
|
|
18
|
+
*
|
|
19
|
+
* The session endpoint is expected to call Square's
|
|
20
|
+
* `POST /v2/online-checkout/payment-links` with a `quick_pay` or `order`
|
|
21
|
+
* body and return `{ url }` using the `payment_link.url` value from the
|
|
22
|
+
* response.
|
|
23
|
+
*/
|
|
24
|
+
import type { PaymentProvider } from '@aquarian-metals/coin-moebius-core';
|
|
25
|
+
export interface SquareProviderConfig {
|
|
26
|
+
/** Full URL of the session endpoint that returns `{ url }`. */
|
|
27
|
+
sessionEndpoint: string;
|
|
28
|
+
/** Optional fetch override — used by tests. Defaults to global `fetch`. */
|
|
29
|
+
fetcher?: typeof fetch;
|
|
30
|
+
/** Optional navigation override — used by tests. Defaults to `location.assign`. */
|
|
31
|
+
navigate?: (url: string) => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a `PaymentProvider` registered as `id: 'square'`. Fires `onPending`
|
|
35
|
+
* immediately, then navigates to the Square-hosted checkout URL. Final
|
|
36
|
+
* settlement lands on the server via webhook (HMAC-SHA256, see `./server`).
|
|
37
|
+
*/
|
|
38
|
+
export declare function createSquareProvider(config: SquareProviderConfig): PaymentProvider;
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EACX,eAAe,EAGf,MAAM,oCAAoC,CAAC;AAE5C,MAAM,WAAW,oBAAoB;IACpC,+DAA+D;IAC/D,eAAe,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAQD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,eAAe,CAiDlF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a `PaymentProvider` registered as `id: 'square'`. Fires `onPending`
|
|
3
|
+
* immediately, then navigates to the Square-hosted checkout URL. Final
|
|
4
|
+
* settlement lands on the server via webhook (HMAC-SHA256, see `./server`).
|
|
5
|
+
*/
|
|
6
|
+
export function createSquareProvider(config) {
|
|
7
|
+
const fetcher = config.fetcher ?? globalThis.fetch.bind(globalThis);
|
|
8
|
+
const navigate = config.navigate ??
|
|
9
|
+
((url) => {
|
|
10
|
+
window.location.assign(url);
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
id: 'square',
|
|
14
|
+
name: 'Square',
|
|
15
|
+
async initiate(options, callbacks) {
|
|
16
|
+
try {
|
|
17
|
+
const body = {
|
|
18
|
+
productId: options.productId,
|
|
19
|
+
amount: options.amount,
|
|
20
|
+
currency: options.currency,
|
|
21
|
+
};
|
|
22
|
+
if (options.metadata)
|
|
23
|
+
body.metadata = options.metadata;
|
|
24
|
+
const response = await fetcher(config.sessionEndpoint, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify(body),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(`coin-moebius/square: session endpoint responded ${response.status}`);
|
|
31
|
+
}
|
|
32
|
+
const payload = (await response.json());
|
|
33
|
+
if (!payload.url) {
|
|
34
|
+
throw new Error('coin-moebius/square: session response missing `url`');
|
|
35
|
+
}
|
|
36
|
+
const result = {
|
|
37
|
+
status: 'pending',
|
|
38
|
+
paymentId: payload.paymentId ?? '',
|
|
39
|
+
provider: 'square',
|
|
40
|
+
amount: options.amount,
|
|
41
|
+
currency: options.currency,
|
|
42
|
+
metadata: options.metadata ?? {},
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
};
|
|
45
|
+
callbacks.onPending?.(result);
|
|
46
|
+
navigate(payload.url);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
callbacks.onError(err instanceof Error ? err : new Error(String(err)));
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA4CA;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAA4B;IAChE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpE,MAAM,QAAQ,GACb,MAAM,CAAC,QAAQ;QACf,CAAC,CAAC,GAAW,EAAE,EAAE;YAChB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IAEJ,OAAO;QACN,EAAE,EAAE,QAAQ;QACZ,IAAI,EAAE,QAAQ;QACd,KAAK,CAAC,QAAQ,CAAC,OAAwB,EAAE,SAAS;YACjD,IAAI,CAAC;gBACJ,MAAM,IAAI,GAA4B;oBACrC,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;iBAC1B,CAAC;gBACF,IAAI,OAAO,CAAC,QAAQ;oBAAE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;gBAEvD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE;oBACtD,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;iBAC1B,CAAC,CAAC;gBACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBAClB,MAAM,IAAI,KAAK,CAAC,mDAAmD,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBACvF,CAAC;gBACD,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAoB,CAAC;gBAC3D,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;oBAClB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;gBACxE,CAAC;gBAED,MAAM,MAAM,GAAkB;oBAC7B,MAAM,EAAE,SAAS;oBACjB,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;oBAClC,QAAQ,EAAE,QAAQ;oBAClB,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;oBAChC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACrB,CAAC;gBACF,SAAS,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC;gBAC9B,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,SAAS,CAAC,OAAO,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACxE,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Square server-side webhook verifier. Implements Square's HMAC-SHA256
|
|
3
|
+
* signature scheme as documented at
|
|
4
|
+
* <https://developer.squareup.com/docs/webhooks/step3validate>:
|
|
5
|
+
*
|
|
6
|
+
* X-Square-HmacSha256-Signature: <base64(HMAC-SHA256(notificationUrl + rawBody, signatureKey))>
|
|
7
|
+
*
|
|
8
|
+
* The signature is computed over the **concatenation of the notification
|
|
9
|
+
* URL and the raw body**, with no separator. The `notificationUrl` must
|
|
10
|
+
* exactly match the URL the merchant configured on their webhook
|
|
11
|
+
* subscription in Square's Developer Console. The verifier accepts it as
|
|
12
|
+
* a required config field because workers running behind a reverse proxy
|
|
13
|
+
* or Cloudflare typically can't recover the original public URL from the
|
|
14
|
+
* inbound request.
|
|
15
|
+
*
|
|
16
|
+
* The signature key is **subscription-specific**, generated in Square's
|
|
17
|
+
* Developer Console alongside the subscription. It is not the same as the
|
|
18
|
+
* application's access token. Rotate by recreating the subscription.
|
|
19
|
+
*
|
|
20
|
+
* Status mapping. `null` results mean the event was signature-verified but
|
|
21
|
+
* does not map to a payment-status change consumers should record.
|
|
22
|
+
*
|
|
23
|
+
* payment.created → pending
|
|
24
|
+
* payment.updated (status COMPLETED) → success
|
|
25
|
+
* payment.updated (status APPROVED) → pending (auth-only)
|
|
26
|
+
* payment.updated (status FAILED or CANCELED) → failed
|
|
27
|
+
* refund.created → pending
|
|
28
|
+
* refund.updated (status COMPLETED) → refunded
|
|
29
|
+
* refund.updated (status FAILED) → failed
|
|
30
|
+
* dispute.created → disputed
|
|
31
|
+
* dispute.state.updated → disputed
|
|
32
|
+
* anything else → null
|
|
33
|
+
*
|
|
34
|
+
* **Known gap:** Square's signature scheme has no timestamp component, so
|
|
35
|
+
* the verifier cannot enforce a replay window. Merchants who care about
|
|
36
|
+
* replay protection should deduplicate at the application layer using the
|
|
37
|
+
* webhook's `event_id` field. Documented in the README.
|
|
38
|
+
*/
|
|
39
|
+
import type { WebhookEvent } from '@aquarian-metals/coin-moebius-core';
|
|
40
|
+
export interface SquareVerifierConfig {
|
|
41
|
+
/**
|
|
42
|
+
* The subscription's signature key from Square's Developer Console
|
|
43
|
+
* (Webhooks → your subscription → Signature key).
|
|
44
|
+
*/
|
|
45
|
+
signatureKey: string;
|
|
46
|
+
/**
|
|
47
|
+
* The exact notification URL configured on the Square webhook
|
|
48
|
+
* subscription. Must match byte-for-byte (including scheme, host, port,
|
|
49
|
+
* path, and any trailing slash) — Square HMACs over `notificationUrl +
|
|
50
|
+
* rawBody`, so a mismatch produces silent signature failure.
|
|
51
|
+
*/
|
|
52
|
+
notificationUrl: string;
|
|
53
|
+
}
|
|
54
|
+
export interface WebhookVerifier {
|
|
55
|
+
verify(rawBody: unknown, headers: Record<string, string | undefined>): Promise<WebhookEvent | null>;
|
|
56
|
+
}
|
|
57
|
+
export declare function createSquareVerifier(config: SquareVerifierConfig): WebhookVerifier;
|
|
58
|
+
/**
|
|
59
|
+
* Base64-encoded HMAC-SHA256 of `notificationUrl + rawBody`, keyed by the
|
|
60
|
+
* subscription's signature key. Exported so callers with non-standard
|
|
61
|
+
* rawBody pipelines can verify with the same routine without going through
|
|
62
|
+
* the full registry path.
|
|
63
|
+
*/
|
|
64
|
+
export declare function computeSquareSignature(notificationUrl: string, rawBody: Uint8Array, signatureKey: string): Promise<string>;
|
|
65
|
+
/**
|
|
66
|
+
* Square does not expose a buyer-facing customer portal in its
|
|
67
|
+
* Subscriptions API. Buyers don't manage their own subscriptions on the
|
|
68
|
+
* Square side; the merchant cancels from their Square dashboard, and the
|
|
69
|
+
* buyer is notified by email. We surface the merchant-side dashboard URL
|
|
70
|
+
* so the merchant can drill into a specific subscription for support.
|
|
71
|
+
*
|
|
72
|
+
* `mode` toggles between live and sandbox dashboards. Pass the
|
|
73
|
+
* subscription id to deep-link directly into the row when available.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getSquarePortalUrl(opts?: {
|
|
76
|
+
mode?: 'live' | 'sandbox';
|
|
77
|
+
subscriptionId?: string;
|
|
78
|
+
}): string;
|
|
79
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEtF,MAAM,WAAW,oBAAoB;IACpC;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC/B,MAAM,CACL,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACzC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CAChC;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,eAAe,CAqClF;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC3C,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,UAAU,EACnB,YAAY,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAkVD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,GAAE;IACL,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACnB,GACJ,MAAM,CAMR"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Square server-side webhook verifier. Implements Square's HMAC-SHA256
|
|
3
|
+
* signature scheme as documented at
|
|
4
|
+
* <https://developer.squareup.com/docs/webhooks/step3validate>:
|
|
5
|
+
*
|
|
6
|
+
* X-Square-HmacSha256-Signature: <base64(HMAC-SHA256(notificationUrl + rawBody, signatureKey))>
|
|
7
|
+
*
|
|
8
|
+
* The signature is computed over the **concatenation of the notification
|
|
9
|
+
* URL and the raw body**, with no separator. The `notificationUrl` must
|
|
10
|
+
* exactly match the URL the merchant configured on their webhook
|
|
11
|
+
* subscription in Square's Developer Console. The verifier accepts it as
|
|
12
|
+
* a required config field because workers running behind a reverse proxy
|
|
13
|
+
* or Cloudflare typically can't recover the original public URL from the
|
|
14
|
+
* inbound request.
|
|
15
|
+
*
|
|
16
|
+
* The signature key is **subscription-specific**, generated in Square's
|
|
17
|
+
* Developer Console alongside the subscription. It is not the same as the
|
|
18
|
+
* application's access token. Rotate by recreating the subscription.
|
|
19
|
+
*
|
|
20
|
+
* Status mapping. `null` results mean the event was signature-verified but
|
|
21
|
+
* does not map to a payment-status change consumers should record.
|
|
22
|
+
*
|
|
23
|
+
* payment.created → pending
|
|
24
|
+
* payment.updated (status COMPLETED) → success
|
|
25
|
+
* payment.updated (status APPROVED) → pending (auth-only)
|
|
26
|
+
* payment.updated (status FAILED or CANCELED) → failed
|
|
27
|
+
* refund.created → pending
|
|
28
|
+
* refund.updated (status COMPLETED) → refunded
|
|
29
|
+
* refund.updated (status FAILED) → failed
|
|
30
|
+
* dispute.created → disputed
|
|
31
|
+
* dispute.state.updated → disputed
|
|
32
|
+
* anything else → null
|
|
33
|
+
*
|
|
34
|
+
* **Known gap:** Square's signature scheme has no timestamp component, so
|
|
35
|
+
* the verifier cannot enforce a replay window. Merchants who care about
|
|
36
|
+
* replay protection should deduplicate at the application layer using the
|
|
37
|
+
* webhook's `event_id` field. Documented in the README.
|
|
38
|
+
*/
|
|
39
|
+
export function createSquareVerifier(config) {
|
|
40
|
+
return {
|
|
41
|
+
async verify(rawBody, headers) {
|
|
42
|
+
if (!config.signatureKey) {
|
|
43
|
+
throw new Error('coin-moebius/square: signatureKey missing on verifier config');
|
|
44
|
+
}
|
|
45
|
+
if (!config.notificationUrl) {
|
|
46
|
+
throw new Error('coin-moebius/square: notificationUrl missing on verifier config');
|
|
47
|
+
}
|
|
48
|
+
const signatureHeader = headerValue(headers, 'x-square-hmacsha256-signature');
|
|
49
|
+
if (!signatureHeader) {
|
|
50
|
+
throw new Error('coin-moebius/square: missing x-square-hmacsha256-signature header');
|
|
51
|
+
}
|
|
52
|
+
const bodyBytes = bodyToBytes(rawBody);
|
|
53
|
+
const expected = await computeSquareSignature(config.notificationUrl, bodyBytes, config.signatureKey);
|
|
54
|
+
if (!timingSafeStringEqual(expected, signatureHeader.trim())) {
|
|
55
|
+
throw new Error('coin-moebius/square: invalid signature');
|
|
56
|
+
}
|
|
57
|
+
const bodyString = new TextDecoder().decode(bodyBytes);
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = JSON.parse(bodyString);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new Error('coin-moebius/square: body is not valid JSON');
|
|
64
|
+
}
|
|
65
|
+
return toPaymentResult(parsed);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Base64-encoded HMAC-SHA256 of `notificationUrl + rawBody`, keyed by the
|
|
71
|
+
* subscription's signature key. Exported so callers with non-standard
|
|
72
|
+
* rawBody pipelines can verify with the same routine without going through
|
|
73
|
+
* the full registry path.
|
|
74
|
+
*/
|
|
75
|
+
export async function computeSquareSignature(notificationUrl, rawBody, signatureKey) {
|
|
76
|
+
const urlBytes = new TextEncoder().encode(notificationUrl);
|
|
77
|
+
const message = new Uint8Array(urlBytes.length + rawBody.length);
|
|
78
|
+
message.set(urlBytes, 0);
|
|
79
|
+
message.set(rawBody, urlBytes.length);
|
|
80
|
+
const keyBytes = new TextEncoder().encode(signatureKey);
|
|
81
|
+
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
82
|
+
const sigBuf = await crypto.subtle.sign('HMAC', key, message);
|
|
83
|
+
return toBase64(new Uint8Array(sigBuf));
|
|
84
|
+
}
|
|
85
|
+
function toPaymentResult(event) {
|
|
86
|
+
const eventType = event.type ?? '';
|
|
87
|
+
// Subscription lifecycle events take precedence over the one-time
|
|
88
|
+
// payment mapping. Square emits separate top-level event types for
|
|
89
|
+
// subscriptions (no overlap with payment events), so the dispatch is
|
|
90
|
+
// straightforward.
|
|
91
|
+
const subscriptionEvent = toSubscriptionEvent(event, eventType);
|
|
92
|
+
if (subscriptionEvent)
|
|
93
|
+
return subscriptionEvent;
|
|
94
|
+
const data = event.data?.object ?? {};
|
|
95
|
+
const payment = data.payment;
|
|
96
|
+
const refund = data.refund;
|
|
97
|
+
const dispute = data.dispute;
|
|
98
|
+
const status = mapEvent(eventType, payment, refund);
|
|
99
|
+
if (status === null)
|
|
100
|
+
return null;
|
|
101
|
+
const { paymentId, amount, currency } = readDetails(eventType, payment, refund, dispute);
|
|
102
|
+
return {
|
|
103
|
+
kind: 'payment',
|
|
104
|
+
status,
|
|
105
|
+
paymentId,
|
|
106
|
+
provider: 'square',
|
|
107
|
+
amount,
|
|
108
|
+
currency,
|
|
109
|
+
metadata: {
|
|
110
|
+
squareEventType: eventType,
|
|
111
|
+
squareEventId: event.event_id,
|
|
112
|
+
squarePaymentStatus: payment?.status,
|
|
113
|
+
squareRefundStatus: refund?.status,
|
|
114
|
+
squareDisputeState: dispute?.state,
|
|
115
|
+
},
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
raw: event,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Map Square subscription event types onto our normalized
|
|
122
|
+
* `SubscriptionEvent`. Returns `null` for any other event so the caller
|
|
123
|
+
* falls through to the payment-event path.
|
|
124
|
+
*
|
|
125
|
+
* subscription.created → subscription.created
|
|
126
|
+
* invoice.payment_made (subscription-linked) → subscription.renewed
|
|
127
|
+
* invoice.scheduled_charge_failed → subscription.payment_failed
|
|
128
|
+
* subscription.updated → subscription.updated
|
|
129
|
+
* subscription.canceled → subscription.canceled
|
|
130
|
+
*/
|
|
131
|
+
function toSubscriptionEvent(event, eventType) {
|
|
132
|
+
const innerObject = (event.data?.object ?? {});
|
|
133
|
+
let subscriptionType = null;
|
|
134
|
+
switch (eventType) {
|
|
135
|
+
case 'subscription.created':
|
|
136
|
+
subscriptionType = 'subscription.created';
|
|
137
|
+
break;
|
|
138
|
+
case 'subscription.updated':
|
|
139
|
+
subscriptionType = 'subscription.updated';
|
|
140
|
+
break;
|
|
141
|
+
case 'subscription.canceled':
|
|
142
|
+
case 'subscription.deactivated':
|
|
143
|
+
subscriptionType = 'subscription.canceled';
|
|
144
|
+
break;
|
|
145
|
+
case 'invoice.payment_made':
|
|
146
|
+
if (innerObject.invoice?.subscription_id)
|
|
147
|
+
subscriptionType = 'subscription.renewed';
|
|
148
|
+
break;
|
|
149
|
+
case 'invoice.scheduled_charge_failed':
|
|
150
|
+
if (innerObject.invoice?.subscription_id)
|
|
151
|
+
subscriptionType = 'subscription.payment_failed';
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (subscriptionType === null)
|
|
157
|
+
return null;
|
|
158
|
+
const subscription = innerObject.subscription;
|
|
159
|
+
const invoice = innerObject.invoice;
|
|
160
|
+
const subscriptionId = subscription?.id ?? invoice?.subscription_id ?? '';
|
|
161
|
+
if (!subscriptionId)
|
|
162
|
+
return null;
|
|
163
|
+
const status = mapSquareSubscriptionStatus(eventType, subscription?.status);
|
|
164
|
+
const { amount, currency } = readSquareSubscriptionAmount(invoice);
|
|
165
|
+
const charged = subscription?.charged_through_date;
|
|
166
|
+
// Square's `charged_through_date` is a calendar date (`YYYY-MM-DD`).
|
|
167
|
+
// Treat it as midnight UTC of the day AFTER, since that's when the
|
|
168
|
+
// next renewal becomes due.
|
|
169
|
+
const currentPeriodEnd = parseChargedThroughDate(charged);
|
|
170
|
+
return {
|
|
171
|
+
kind: 'subscription',
|
|
172
|
+
type: subscriptionType,
|
|
173
|
+
subscriptionId,
|
|
174
|
+
provider: 'square',
|
|
175
|
+
productId: typeof subscription?.plan_variation_id === 'string'
|
|
176
|
+
? subscription.plan_variation_id
|
|
177
|
+
: typeof subscription?.plan_id === 'string'
|
|
178
|
+
? subscription.plan_id
|
|
179
|
+
: null,
|
|
180
|
+
customerRef: typeof subscription?.customer_id === 'string' ? subscription.customer_id : null,
|
|
181
|
+
status,
|
|
182
|
+
currentPeriodEnd,
|
|
183
|
+
amount,
|
|
184
|
+
currency,
|
|
185
|
+
metadata: {
|
|
186
|
+
squareEventType: eventType,
|
|
187
|
+
squareEventId: event.event_id,
|
|
188
|
+
},
|
|
189
|
+
timestamp: Date.now(),
|
|
190
|
+
raw: event,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function mapSquareSubscriptionStatus(eventType, rawStatus) {
|
|
194
|
+
if (eventType === 'subscription.canceled' || eventType === 'subscription.deactivated') {
|
|
195
|
+
return 'canceled';
|
|
196
|
+
}
|
|
197
|
+
if (eventType === 'invoice.scheduled_charge_failed')
|
|
198
|
+
return 'past_due';
|
|
199
|
+
switch (rawStatus) {
|
|
200
|
+
case 'ACTIVE':
|
|
201
|
+
return 'active';
|
|
202
|
+
case 'PAUSED':
|
|
203
|
+
return 'paused';
|
|
204
|
+
case 'CANCELED':
|
|
205
|
+
case 'DEACTIVATED':
|
|
206
|
+
return 'canceled';
|
|
207
|
+
default:
|
|
208
|
+
return 'unknown';
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function readSquareSubscriptionAmount(invoice) {
|
|
212
|
+
const money = invoice?.payment_requests?.[0]?.computed_amount_money;
|
|
213
|
+
const cents = money?.amount;
|
|
214
|
+
const amount = typeof cents === 'number' ? cents / 100 : 0;
|
|
215
|
+
const currency = (money?.currency ?? 'USD').toUpperCase();
|
|
216
|
+
return { amount, currency };
|
|
217
|
+
}
|
|
218
|
+
function parseChargedThroughDate(value) {
|
|
219
|
+
if (typeof value !== 'string')
|
|
220
|
+
return null;
|
|
221
|
+
const ms = Date.parse(`${value}T00:00:00Z`);
|
|
222
|
+
return Number.isFinite(ms) ? Math.floor(ms / 1000) : null;
|
|
223
|
+
}
|
|
224
|
+
function mapEvent(eventType, payment, refund) {
|
|
225
|
+
if (eventType === 'payment.created')
|
|
226
|
+
return 'pending';
|
|
227
|
+
if (eventType === 'payment.updated') {
|
|
228
|
+
const innerStatus = payment?.status;
|
|
229
|
+
if (innerStatus === 'COMPLETED')
|
|
230
|
+
return 'success';
|
|
231
|
+
if (innerStatus === 'APPROVED')
|
|
232
|
+
return 'pending';
|
|
233
|
+
if (innerStatus === 'FAILED' || innerStatus === 'CANCELED')
|
|
234
|
+
return 'failed';
|
|
235
|
+
// Unknown inner status — surface as pending so consumers don't miss
|
|
236
|
+
// it; they can inspect `raw.data.object.payment.status` for detail.
|
|
237
|
+
return 'pending';
|
|
238
|
+
}
|
|
239
|
+
if (eventType === 'refund.created')
|
|
240
|
+
return 'pending';
|
|
241
|
+
if (eventType === 'refund.updated') {
|
|
242
|
+
const innerStatus = refund?.status;
|
|
243
|
+
if (innerStatus === 'COMPLETED')
|
|
244
|
+
return 'refunded';
|
|
245
|
+
if (innerStatus === 'FAILED' || innerStatus === 'REJECTED')
|
|
246
|
+
return 'failed';
|
|
247
|
+
return 'pending';
|
|
248
|
+
}
|
|
249
|
+
if (eventType === 'dispute.created' || eventType === 'dispute.state.updated') {
|
|
250
|
+
return 'disputed';
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
function readDetails(eventType, payment, refund, dispute) {
|
|
255
|
+
// Prefer the original payment id as the stable correlation key across
|
|
256
|
+
// payment / refund / dispute events on the same purchase.
|
|
257
|
+
const correlationPaymentId = payment?.id ?? refund?.payment_id ?? dispute?.disputed_payment?.payment_id ?? '';
|
|
258
|
+
let money;
|
|
259
|
+
if (eventType.startsWith('payment.')) {
|
|
260
|
+
money = payment?.amount_money;
|
|
261
|
+
}
|
|
262
|
+
else if (eventType.startsWith('refund.')) {
|
|
263
|
+
money = refund?.amount_money;
|
|
264
|
+
}
|
|
265
|
+
else if (eventType.startsWith('dispute.')) {
|
|
266
|
+
money = dispute?.amount_money;
|
|
267
|
+
}
|
|
268
|
+
// Square uses smallest-currency-unit integers (cents for USD, etc.).
|
|
269
|
+
// Convert to a major-unit decimal here to match the rest of the SDK's
|
|
270
|
+
// `amount` semantics (Stripe verifier does the same `/100`).
|
|
271
|
+
const minorAmount = typeof money?.amount === 'number' ? money.amount : 0;
|
|
272
|
+
const currency = (money?.currency ?? 'USD').toUpperCase();
|
|
273
|
+
const amount = minorAmount / 100;
|
|
274
|
+
return { paymentId: correlationPaymentId, amount, currency };
|
|
275
|
+
}
|
|
276
|
+
// --- helpers ---------------------------------------------------------------
|
|
277
|
+
function bodyToBytes(rawBody) {
|
|
278
|
+
if (rawBody instanceof Uint8Array)
|
|
279
|
+
return rawBody;
|
|
280
|
+
if (typeof rawBody === 'string')
|
|
281
|
+
return new TextEncoder().encode(rawBody);
|
|
282
|
+
if (rawBody && typeof rawBody === 'object') {
|
|
283
|
+
return new TextEncoder().encode(JSON.stringify(rawBody));
|
|
284
|
+
}
|
|
285
|
+
throw new Error('coin-moebius/square: unsupported body type');
|
|
286
|
+
}
|
|
287
|
+
function headerValue(headers, name) {
|
|
288
|
+
const lower = name.toLowerCase();
|
|
289
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
290
|
+
if (key.toLowerCase() === lower)
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
function timingSafeStringEqual(a, b) {
|
|
296
|
+
if (a.length !== b.length)
|
|
297
|
+
return false;
|
|
298
|
+
let mismatch = 0;
|
|
299
|
+
for (let i = 0; i < a.length; i++) {
|
|
300
|
+
mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
301
|
+
}
|
|
302
|
+
return mismatch === 0;
|
|
303
|
+
}
|
|
304
|
+
function toBase64(bytes) {
|
|
305
|
+
let binary = '';
|
|
306
|
+
for (const b of bytes)
|
|
307
|
+
binary += String.fromCharCode(b);
|
|
308
|
+
return btoa(binary);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Square does not expose a buyer-facing customer portal in its
|
|
312
|
+
* Subscriptions API. Buyers don't manage their own subscriptions on the
|
|
313
|
+
* Square side; the merchant cancels from their Square dashboard, and the
|
|
314
|
+
* buyer is notified by email. We surface the merchant-side dashboard URL
|
|
315
|
+
* so the merchant can drill into a specific subscription for support.
|
|
316
|
+
*
|
|
317
|
+
* `mode` toggles between live and sandbox dashboards. Pass the
|
|
318
|
+
* subscription id to deep-link directly into the row when available.
|
|
319
|
+
*/
|
|
320
|
+
export function getSquarePortalUrl(opts = {}) {
|
|
321
|
+
const base = opts.mode === 'sandbox'
|
|
322
|
+
? 'https://app.squareupsandbox.com/dashboard/subscriptions'
|
|
323
|
+
: 'https://squareup.com/dashboard/subscriptions';
|
|
324
|
+
return opts.subscriptionId ? `${base}/${opts.subscriptionId}` : base;
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AA0BH,MAAM,UAAU,oBAAoB,CAAC,MAA4B;IAChE,OAAO;QACN,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO;YAC5B,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;YACjF,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;YACpF,CAAC;YAED,MAAM,eAAe,GAAG,WAAW,CAAC,OAAO,EAAE,+BAA+B,CAAC,CAAC;YAC9E,IAAI,CAAC,eAAe,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;YACtF,CAAC;YAED,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAC5C,MAAM,CAAC,eAAe,EACtB,SAAS,EACT,MAAM,CAAC,YAAY,CACnB,CAAC;YAEF,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC9D,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC3D,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,MAA0B,CAAC;YAC/B,IAAI,CAAC;gBACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAuB,CAAC;YACvD,CAAC;YAAC,MAAM,CAAC;gBACR,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;YAChE,CAAC;YAED,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;KACD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,eAAuB,EACvB,OAAmB,EACnB,YAAoB;IAEpB,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEtC,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACxC,KAAK,EACL,QAAwB,EACxB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACR,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9D,OAAO,QAAQ,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AACzC,CAAC;AAoDD,SAAS,eAAe,CAAC,KAAyB;IACjD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;IAEnC,kEAAkE;IAClE,mEAAmE;IACnE,qEAAqE;IACrE,mBAAmB;IACnB,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAChE,IAAI,iBAAiB;QAAE,OAAO,iBAAiB,CAAC;IAEhD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAE7B,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACpD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAEzF,OAAO;QACN,IAAI,EAAE,SAAS;QACf,MAAM;QACN,SAAS;QACT,QAAQ,EAAE,QAAQ;QAClB,MAAM;QACN,QAAQ;QACR,QAAQ,EAAE;YACT,eAAe,EAAE,SAAS;YAC1B,aAAa,EAAE,KAAK,CAAC,QAAQ;YAC7B,mBAAmB,EAAE,OAAO,EAAE,MAAM;YACpC,kBAAkB,EAAE,MAAM,EAAE,MAAM;YAClC,kBAAkB,EAAE,OAAO,EAAE,KAAK;SAClC;QACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,GAAG,EAAE,KAAK;KACV,CAAC;AACH,CAAC;AA0BD;;;;;;;;;;GAUG;AACH,SAAS,mBAAmB,CAAC,KAAyB,EAAE,SAAiB;IACxE,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAG5C,CAAC;IAEF,IAAI,gBAAgB,GAMV,IAAI,CAAC;IAEf,QAAQ,SAAS,EAAE,CAAC;QACnB,KAAK,sBAAsB;YAC1B,gBAAgB,GAAG,sBAAsB,CAAC;YAC1C,MAAM;QACP,KAAK,sBAAsB;YAC1B,gBAAgB,GAAG,sBAAsB,CAAC;YAC1C,MAAM;QACP,KAAK,uBAAuB,CAAC;QAC7B,KAAK,0BAA0B;YAC9B,gBAAgB,GAAG,uBAAuB,CAAC;YAC3C,MAAM;QACP,KAAK,sBAAsB;YAC1B,IAAI,WAAW,CAAC,OAAO,EAAE,eAAe;gBAAE,gBAAgB,GAAG,sBAAsB,CAAC;YACpF,MAAM;QACP,KAAK,iCAAiC;YACrC,IAAI,WAAW,CAAC,OAAO,EAAE,eAAe;gBAAE,gBAAgB,GAAG,6BAA6B,CAAC;YAC3F,MAAM;QACP;YACC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,gBAAgB,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE3C,MAAM,YAAY,GAAG,WAAW,CAAC,YAAY,CAAC;IAC9C,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;IACpC,MAAM,cAAc,GAAG,YAAY,EAAE,EAAE,IAAI,OAAO,EAAE,eAAe,IAAI,EAAE,CAAC;IAC1E,IAAI,CAAC,cAAc;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,MAAM,GAAG,2BAA2B,CAAC,SAAS,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAC5E,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,YAAY,EAAE,oBAAoB,CAAC;IACnD,qEAAqE;IACrE,mEAAmE;IACnE,4BAA4B;IAC5B,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAE1D,OAAO;QACN,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,gBAAgB;QACtB,cAAc;QACd,QAAQ,EAAE,QAAQ;QAClB,SAAS,EACR,OAAO,YAAY,EAAE,iBAAiB,KAAK,QAAQ;YAClD,CAAC,CAAC,YAAY,CAAC,iBAAiB;YAChC,CAAC,CAAC,OAAO,YAAY,EAAE,OAAO,KAAK,QAAQ;gBAC1C,CAAC,CAAC,YAAY,CAAC,OAAO;gBACtB,CAAC,CAAC,IAAI;QACT,WAAW,EAAE,OAAO,YAAY,EAAE,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;QAC5F,MAAM;QACN,gBAAgB;QAChB,MAAM;QACN,QAAQ;QACR,QAAQ,EAAE;YACT,eAAe,EAAE,SAAS;YAC1B,aAAa,EAAE,KAAK,CAAC,QAAQ;SAC7B;QACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,GAAG,EAAE,KAAK;KACV,CAAC;AACH,CAAC;AAED,SAAS,2BAA2B,CACnC,SAAiB,EACjB,SAA6B;IAE7B,IAAI,SAAS,KAAK,uBAAuB,IAAI,SAAS,KAAK,0BAA0B,EAAE,CAAC;QACvF,OAAO,UAAU,CAAC;IACnB,CAAC;IACD,IAAI,SAAS,KAAK,iCAAiC;QAAE,OAAO,UAAU,CAAC;IACvE,QAAQ,SAAS,EAAE,CAAC;QACnB,KAAK,QAAQ;YACZ,OAAO,QAAQ,CAAC;QACjB,KAAK,QAAQ;YACZ,OAAO,QAAQ,CAAC;QACjB,KAAK,UAAU,CAAC;QAChB,KAAK,aAAa;YACjB,OAAO,UAAU,CAAC;QACnB;YACC,OAAO,SAAS,CAAC;IACnB,CAAC;AACF,CAAC;AAED,SAAS,4BAA4B,CAAC,OAAwC;IAI7E,MAAM,KAAK,GAAG,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC;IACpE,MAAM,KAAK,GAAG,KAAK,EAAE,MAAM,CAAC;IAC5B,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,QAAQ,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAyB;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3D,CAAC;AAED,SAAS,QAAQ,CAChB,SAAiB,EACjB,OAAwC,EACxC,MAAsC;IAEtC,IAAI,SAAS,KAAK,iBAAiB;QAAE,OAAO,SAAS,CAAC;IAEtD,IAAI,SAAS,KAAK,iBAAiB,EAAE,CAAC;QACrC,MAAM,WAAW,GAAG,OAAO,EAAE,MAAM,CAAC;QACpC,IAAI,WAAW,KAAK,WAAW;YAAE,OAAO,SAAS,CAAC;QAClD,IAAI,WAAW,KAAK,UAAU;YAAE,OAAO,SAAS,CAAC;QACjD,IAAI,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,UAAU;YAAE,OAAO,QAAQ,CAAC;QAC5E,oEAAoE;QACpE,oEAAoE;QACpE,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,IAAI,SAAS,KAAK,gBAAgB;QAAE,OAAO,SAAS,CAAC;IAErD,IAAI,SAAS,KAAK,gBAAgB,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,MAAM,EAAE,MAAM,CAAC;QACnC,IAAI,WAAW,KAAK,WAAW;YAAE,OAAO,UAAU,CAAC;QACnD,IAAI,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,UAAU;YAAE,OAAO,QAAQ,CAAC;QAC5E,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,IAAI,SAAS,KAAK,iBAAiB,IAAI,SAAS,KAAK,uBAAuB,EAAE,CAAC;QAC9E,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CACnB,SAAiB,EACjB,OAAwC,EACxC,MAAsC,EACtC,OAAwC;IAExC,sEAAsE;IACtE,0DAA0D;IAC1D,MAAM,oBAAoB,GACzB,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,UAAU,IAAI,OAAO,EAAE,gBAAgB,EAAE,UAAU,IAAI,EAAE,CAAC;IAElF,IAAI,KAA8B,CAAC;IACnC,IAAI,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACtC,KAAK,GAAG,OAAO,EAAE,YAAY,CAAC;IAC/B,CAAC;SAAM,IAAI,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5C,KAAK,GAAG,MAAM,EAAE,YAAY,CAAC;IAC9B,CAAC;SAAM,IAAI,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,KAAK,GAAG,OAAO,EAAE,YAAY,CAAC;IAC/B,CAAC;IAED,qEAAqE;IACrE,sEAAsE;IACtE,6DAA6D;IAC7D,MAAM,WAAW,GAAG,OAAO,KAAK,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,QAAQ,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1D,MAAM,MAAM,GAAG,WAAW,GAAG,GAAG,CAAC;IAEjC,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9D,CAAC;AAED,8EAA8E;AAE9E,SAAS,WAAW,CAAC,OAAgB;IACpC,IAAI,OAAO,YAAY,UAAU;QAAE,OAAO,OAAO,CAAC;IAClD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1E,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,WAAW,CACnB,OAA2C,EAC3C,IAAY;IAEZ,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;IAC/C,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,qBAAqB,CAAC,CAAS,EAAE,CAAS;IAClD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,QAAQ,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,QAAQ,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAiB;IAClC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CACjC,OAGI,EAAE;IAEN,MAAM,IAAI,GACT,IAAI,CAAC,IAAI,KAAK,SAAS;QACtB,CAAC,CAAC,yDAAyD;QAC3D,CAAC,CAAC,8CAA8C,CAAC;IACnD,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aquarian-metals/coin-moebius-square",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Square (Block) provider for Coin Moebius — Payment Link redirect flow, plus a server-only HMAC-SHA256 webhook verifier.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"payments",
|
|
7
|
+
"square",
|
|
8
|
+
"block",
|
|
9
|
+
"payment-link",
|
|
10
|
+
"checkout",
|
|
11
|
+
"webhook",
|
|
12
|
+
"coin-moebius"
|
|
13
|
+
],
|
|
14
|
+
"author": "aquarian-metals",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"homepage": "https://github.com/aquarian-metals/coin-moebius#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/aquarian-metals/coin-moebius/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/aquarian-metals/coin-moebius.git",
|
|
23
|
+
"directory": "packages/providers/square"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"import": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./server": {
|
|
38
|
+
"types": "./dist/server.d.ts",
|
|
39
|
+
"import": "./dist/server.js"
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
45
|
+
"build": "npm run clean && tsc",
|
|
46
|
+
"prepublishOnly": "npm run build"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@aquarian-metals/coin-moebius-core": "^1.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"typescript": "~5.8.0"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
}
|
|
60
|
+
}
|