@hanzo/pay 0.1.1 → 0.1.2

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 ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -83,5 +83,3 @@ exports.cancelled = cancelled;
83
83
  exports.legible = legible;
84
84
  exports.luminance = luminance;
85
85
  exports.ratio = ratio;
86
- //# sourceMappingURL=chunk-AJAETCMO.cjs.map
87
- //# sourceMappingURL=chunk-AJAETCMO.cjs.map
@@ -74,5 +74,3 @@ function legible(p) {
74
74
  }
75
75
 
76
76
  export { Cancelled, HEX, PALETTE, REACH, cancelled, legible, luminance, ratio };
77
- //# sourceMappingURL=chunk-IYAU5BMM.js.map
78
- //# sourceMappingURL=chunk-IYAU5BMM.js.map
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkAJAETCMO_cjs = require('./chunk-AJAETCMO.cjs');
3
+ var chunkZL22HE3V_cjs = require('./chunk-ZL22HE3V.cjs');
4
4
 
5
5
  // src/attempt.ts
6
6
  var LIMIT = 45;
@@ -26,40 +26,38 @@ function clamp(key, reserved = 25) {
26
26
 
27
27
  Object.defineProperty(exports, "Cancelled", {
28
28
  enumerable: true,
29
- get: function () { return chunkAJAETCMO_cjs.Cancelled; }
29
+ get: function () { return chunkZL22HE3V_cjs.Cancelled; }
30
30
  });
31
31
  Object.defineProperty(exports, "HEX", {
32
32
  enumerable: true,
33
- get: function () { return chunkAJAETCMO_cjs.HEX; }
33
+ get: function () { return chunkZL22HE3V_cjs.HEX; }
34
34
  });
35
35
  Object.defineProperty(exports, "PALETTE", {
36
36
  enumerable: true,
37
- get: function () { return chunkAJAETCMO_cjs.PALETTE; }
37
+ get: function () { return chunkZL22HE3V_cjs.PALETTE; }
38
38
  });
39
39
  Object.defineProperty(exports, "REACH", {
40
40
  enumerable: true,
41
- get: function () { return chunkAJAETCMO_cjs.REACH; }
41
+ get: function () { return chunkZL22HE3V_cjs.REACH; }
42
42
  });
43
43
  Object.defineProperty(exports, "cancelled", {
44
44
  enumerable: true,
45
- get: function () { return chunkAJAETCMO_cjs.cancelled; }
45
+ get: function () { return chunkZL22HE3V_cjs.cancelled; }
46
46
  });
47
47
  Object.defineProperty(exports, "legible", {
48
48
  enumerable: true,
49
- get: function () { return chunkAJAETCMO_cjs.legible; }
49
+ get: function () { return chunkZL22HE3V_cjs.legible; }
50
50
  });
51
51
  Object.defineProperty(exports, "luminance", {
52
52
  enumerable: true,
53
- get: function () { return chunkAJAETCMO_cjs.luminance; }
53
+ get: function () { return chunkZL22HE3V_cjs.luminance; }
54
54
  });
55
55
  Object.defineProperty(exports, "ratio", {
56
56
  enumerable: true,
57
- get: function () { return chunkAJAETCMO_cjs.ratio; }
57
+ get: function () { return chunkZL22HE3V_cjs.ratio; }
58
58
  });
59
59
  exports.CEILING = CEILING;
60
60
  exports.FLOOR = FLOOR;
61
61
  exports.LIMIT = LIMIT;
62
62
  exports.attempt = attempt;
63
63
  exports.clamp = clamp;
64
- //# sourceMappingURL=index.cjs.map
65
- //# sourceMappingURL=index.cjs.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Cancelled, HEX, PALETTE, REACH, cancelled, legible, luminance, ratio } from './chunk-IYAU5BMM.js';
1
+ export { Cancelled, HEX, PALETTE, REACH, cancelled, legible, luminance, ratio } from './chunk-ZVUFN3RQ.js';
2
2
 
3
3
  // src/attempt.ts
4
4
  var LIMIT = 45;
@@ -23,5 +23,3 @@ function clamp(key, reserved = 25) {
23
23
  }
24
24
 
25
25
  export { CEILING, FLOOR, LIMIT, attempt, clamp };
26
- //# sourceMappingURL=index.js.map
27
- //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkAJAETCMO_cjs = require('../chunk-AJAETCMO.cjs');
3
+ var chunkZL22HE3V_cjs = require('../chunk-ZL22HE3V.cjs');
4
4
  var reactNativeSquareInAppPayments = require('react-native-square-in-app-payments');
5
5
 
6
6
  // src/native/theme.ts
@@ -83,10 +83,10 @@ var Native = class {
83
83
  if (await reactNativeSquareInAppPayments.SQIPGooglePay.canUseGooglePay()) out.push("google_pay");
84
84
  } catch {
85
85
  }
86
- return out.filter((m) => chunkAJAETCMO_cjs.REACH.native.includes(m));
86
+ return out.filter((m) => chunkZL22HE3V_cjs.REACH.native.includes(m));
87
87
  }
88
88
  collect(method, tender, detail) {
89
- if (!chunkAJAETCMO_cjs.REACH.native.includes(method)) {
89
+ if (!chunkZL22HE3V_cjs.REACH.native.includes(method)) {
90
90
  return Promise.reject(new Error(`${method} is not a rail this SDK has`));
91
91
  }
92
92
  if (method === "card" || method === "gift") return this.sheet(method, detail);
@@ -119,7 +119,7 @@ var Native = class {
119
119
  }
120
120
  return { success: true, onCardEntryComplete: () => resolve(t) };
121
121
  };
122
- const onCancel = () => reject(new chunkAJAETCMO_cjs.Cancelled(method));
122
+ const onCancel = () => reject(new chunkZL22HE3V_cjs.Cancelled(method));
123
123
  if (method === "gift") reactNativeSquareInAppPayments.SQIPCardEntry.startGiftCardEntryFlow(onNonce, onCancel);
124
124
  else reactNativeSquareInAppPayments.SQIPCardEntry.startCardEntryFlow(false, onNonce, onCancel);
125
125
  });
@@ -161,7 +161,7 @@ var Native = class {
161
161
  // silently be `any`.
162
162
  (state, errorMessage) => {
163
163
  if (state === reactNativeSquareInAppPayments.ApplePayNonceSuccessState.Succeeded && settled) resolve(settled);
164
- else if (state === reactNativeSquareInAppPayments.ApplePayNonceSuccessState.Canceled) reject(new chunkAJAETCMO_cjs.Cancelled("apple_pay"));
164
+ else if (state === reactNativeSquareInAppPayments.ApplePayNonceSuccessState.Canceled) reject(new chunkZL22HE3V_cjs.Cancelled("apple_pay"));
165
165
  else reject(new Error(errorMessage || "Apple Pay could not be completed"));
166
166
  }
167
167
  );
@@ -193,7 +193,7 @@ var Native = class {
193
193
  resolve(t);
194
194
  },
195
195
  (e) => reject(new Error(e.message)),
196
- () => reject(new chunkAJAETCMO_cjs.Cancelled("google_pay"))
196
+ () => reject(new chunkZL22HE3V_cjs.Cancelled("google_pay"))
197
197
  );
198
198
  });
199
199
  }
@@ -210,7 +210,7 @@ async function terminal(config) {
210
210
  }
211
211
  reactNativeSquareInAppPayments.SQIPCore.setSquareApplicationId(config.applicationId);
212
212
  reactNativeSquareInAppPayments.SQIPCardEntry.setIOSCardEntryTheme(
213
- appearance(config.palette ?? chunkAJAETCMO_cjs.PALETTE.dark, config.theme ?? "dark")
213
+ appearance(config.palette ?? chunkZL22HE3V_cjs.PALETTE.dark, config.theme ?? "dark")
214
214
  );
215
215
  if (config.appleMerchantId) reactNativeSquareInAppPayments.SQIPApplePay.initializeApplePay(config.appleMerchantId);
216
216
  reactNativeSquareInAppPayments.SQIPGooglePay.initializeGooglePay(
@@ -223,5 +223,3 @@ async function terminal(config) {
223
223
  exports.appearance = appearance;
224
224
  exports.colour = colour;
225
225
  exports.terminal = terminal;
226
- //# sourceMappingURL=index.cjs.map
227
- //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- import { PALETTE, REACH, Cancelled } from '../chunk-IYAU5BMM.js';
1
+ import { PALETTE, REACH, Cancelled } from '../chunk-ZVUFN3RQ.js';
2
2
  import { Brand, SQIPCore, SQIPCardEntry, SQIPApplePay, SQIPGooglePay, GooglePayEnvironment, ApplePayNonceSuccessState, GooglePayPriceStatus } from 'react-native-square-in-app-payments';
3
3
 
4
4
  // src/native/theme.ts
@@ -219,5 +219,3 @@ async function terminal(config) {
219
219
  }
220
220
 
221
221
  export { appearance, colour, terminal };
222
- //# sourceMappingURL=index.js.map
223
- //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkAJAETCMO_cjs = require('../chunk-AJAETCMO.cjs');
3
+ var chunkZL22HE3V_cjs = require('../chunk-ZL22HE3V.cjs');
4
4
 
5
5
  // src/web/sdk.ts
6
6
  var PRODUCTION = "https://web.squarecdn.com/v1/square.js";
@@ -93,7 +93,7 @@ function pin(p, doc) {
93
93
  // src/web/terminal.ts
94
94
  var DRAWN = ["card", "gift", "google_pay", "cash_app"];
95
95
  function token(method, r) {
96
- if (String(r.status).toUpperCase() === "CANCEL") throw new chunkAJAETCMO_cjs.Cancelled(method);
96
+ if (String(r.status).toUpperCase() === "CANCEL") throw new chunkZL22HE3V_cjs.Cancelled(method);
97
97
  if (r.status !== "OK" || !r.token) {
98
98
  throw new Error(r.errors?.[0]?.message ?? `${method} could not be completed`);
99
99
  }
@@ -138,7 +138,7 @@ var Web = class {
138
138
  */
139
139
  async offers(tender) {
140
140
  const out = [];
141
- for (const method of chunkAJAETCMO_cjs.REACH.web) {
141
+ for (const method of chunkZL22HE3V_cjs.REACH.web) {
142
142
  try {
143
143
  this.built.set(method, await this.build(method, tender));
144
144
  out.push(method);
@@ -216,7 +216,7 @@ async function terminal(config) {
216
216
  if (!config.applicationId || !config.locationId) {
217
217
  throw new Error("Square is not configured for this deployment");
218
218
  }
219
- const palette = config.palette ?? chunkAJAETCMO_cjs.PALETTE.dark;
219
+ const palette = config.palette ?? chunkZL22HE3V_cjs.PALETTE.dark;
220
220
  pin(palette);
221
221
  const src = (config.environment ?? "production").toLowerCase() === "sandbox" ? SANDBOX : PRODUCTION;
222
222
  await load(src);
@@ -235,5 +235,3 @@ exports.pin = pin;
235
235
  exports.rules = rules;
236
236
  exports.style = style;
237
237
  exports.terminal = terminal;
238
- //# sourceMappingURL=index.cjs.map
239
- //# sourceMappingURL=index.cjs.map
package/dist/web/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { PALETTE, REACH, Cancelled } from '../chunk-IYAU5BMM.js';
1
+ import { PALETTE, REACH, Cancelled } from '../chunk-ZVUFN3RQ.js';
2
2
 
3
3
  // src/web/sdk.ts
4
4
  var PRODUCTION = "https://web.squarecdn.com/v1/square.js";
@@ -226,5 +226,3 @@ async function terminal(config) {
226
226
  }
227
227
 
228
228
  export { PRODUCTION, SANDBOX, forget, load, pin, rules, style, terminal };
229
- //# sourceMappingURL=index.js.map
230
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/pay",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Square payments for web and native behind one interface. Card, Apple Pay, Google Pay, Cash App Pay, ACH and gift cards, feature-detected — with one colour table that dresses the web iframe and the native sheet alike.",
6
6
  "license": "Apache-2.0",
@@ -38,16 +38,8 @@
38
38
  "files": [
39
39
  "dist"
40
40
  ],
41
- "scripts": {
42
- "build": "tsup && tsc -p tsconfig.build.json",
43
- "clean": "rm -rf dist",
44
- "prepack": "pnpm run build",
45
- "tc": "tsc --noEmit",
46
- "test": "vitest run",
47
- "test:watch": "vitest"
48
- },
49
41
  "peerDependencies": {
50
- "react-native-square-in-app-payments": ">=2.1.0"
42
+ "react-native-square-in-app-payments": ">=2"
51
43
  },
52
44
  "peerDependenciesMeta": {
53
45
  "react-native-square-in-app-payments": {
@@ -55,10 +47,10 @@
55
47
  }
56
48
  },
57
49
  "devDependencies": {
58
- "react-native-square-in-app-payments": "2.1.0",
50
+ "react-native-square-in-app-payments": "^2.1.1",
59
51
  "tsup": "^8.5.1",
60
- "typescript": "^5.7.2",
61
- "vitest": "^4.0.0"
52
+ "typescript": "7.0.2",
53
+ "vitest": "^4.1.11"
62
54
  },
63
55
  "publishConfig": {
64
56
  "access": "public"
@@ -71,5 +63,12 @@
71
63
  "google-pay",
72
64
  "cash-app-pay",
73
65
  "ach"
74
- ]
75
- }
66
+ ],
67
+ "scripts": {
68
+ "build": "tsup && tsc -p tsconfig.build.json",
69
+ "clean": "rm -rf dist",
70
+ "tc": "tsc --noEmit",
71
+ "test": "vitest run",
72
+ "test:watch": "vitest"
73
+ }
74
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/terminal.ts","../src/palette.ts"],"names":[],"mappings":"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","file":"chunk-AJAETCMO.cjs","sourcesContent":["// What a checkout asks for, what it gets back, and the one thing that turns the\n// first into the second. Platform-agnostic: no DOM, no react-native, no Square.\n//\n// THIS PACKAGE NEVER MOVES MONEY. Every rail below ends at a single-use token,\n// and the charge is a server call the host makes with that token. Nothing here\n// completes a payment, which is why the noun is `Tender` — an OFFER of payment,\n// the term Square's own Orders API uses — and not `Charge`, which would name\n// something this code cannot do.\n\n/**\n * A Square rail.\n *\n * `gift` is a Square gift card, which tokenizes like a card but through its own\n * constructor on both platforms. Non-Square rails a host may also offer (wire,\n * crypto, another processor) are deliberately absent: this package knows Square,\n * and a host that knows more merges its own set with what `offers()` returns.\n */\nexport type Method = 'card' | 'apple_pay' | 'google_pay' | 'cash_app' | 'ach' | 'gift'\n\n/**\n * Money as a DECIMAL STRING, never a number.\n *\n * `0.1 + 0.2` is `0.30000000000000004`, and a float that reaches a payment\n * processor as a total is a rounding error someone is charged. Square's own\n * paymentRequest takes `amount` as a string for this reason; so does this.\n */\nexport interface Money {\n /** Decimal string in major units, e.g. `\"12.00\"`. Never a float. */\n amount: string\n /** ISO 4217, e.g. `\"USD\"`. */\n currency: string\n}\n\n/** An offer of payment: what is being paid, where, and what the buyer is shown. */\nexport interface Tender {\n total: Money\n /** ISO 3166-1 alpha-2 of the MERCHANT, e.g. `\"US\"`. Square keys wallets off it. */\n country: string\n /** The line the wallet sheet shows the buyer, e.g. `\"Hanzo AI credit\"`. */\n label: string\n}\n\n/**\n * A single-use payment token, and the only thing any rail here produces.\n *\n * It is single-use in the strict sense: spending it — charging it OR vaulting it\n * — consumes it. A flow that needs both a charge and a card-on-file must\n * tokenize TWICE; sharing one token between the two fails with the card already\n * accepted, which reads as a decline.\n */\nexport interface Token {\n value: string\n method: Method\n /** Present when the rail reported it. Absent means never reported, not empty. */\n card?: { brand?: string; last4?: string; expMonth?: number; expYear?: number }\n}\n\n/**\n * Rail-specific facts a `Tender` cannot carry, because only one rail needs each.\n * Every field is ignored by the rails it does not belong to.\n */\nexport interface Detail {\n /**\n * ACH only (web), and REQUIRED there: Square matches it against the bank\n * record. Collected from the buyer rather than derived from the account,\n * because the account holder and the person paying are not always the same\n * name.\n */\n name?: string\n /**\n * Card and gift only. Passed straight to Square as verification details —\n * `{ intent, amount, currencyCode, billingContact, customerInitiated,\n * sellerKeyedIn }` — which is how SCA is satisfied in mandated regions.\n * Opaque on purpose: Square owns this shape, and re-declaring it here would\n * be a second copy to drift.\n */\n verify?: Record<string, unknown>\n /**\n * NATIVE ONLY, and the one real asymmetry between the platforms.\n *\n * Square's native card sheet is a state machine, not a form: it STAYS OPEN\n * after minting the token, showing a spinner, until it is told whether the\n * charge worked. Told yes, it closes. Told no, it shows the message ON the\n * sheet with the card still entered, so the buyer retries in place instead of\n * being dropped back to a checkout with an empty form and a decline notice.\n *\n * That is a better decline experience than the web can offer, and it exists\n * only if the host charges DURING the sheet — which is what this is. Given the\n * token, run the server call; return to close the sheet, throw to keep it open\n * with the thrown message shown.\n *\n * Omit it and the sheet closes as soon as the token exists, which is what the\n * web does. Nothing hangs either way.\n *\n * The web backend ignores this: its card fields are a page element with no\n * sheet to hold open.\n */\n charge?(token: Token): Promise<void>\n}\n\n/**\n * The one interface both backends satisfy: web (Square Web Payments SDK) and\n * native (Square In-App Payments SDK). A host writes against this and the\n * platform picks the implementation — see `./web` and `./native`.\n */\nexport interface Terminal {\n /**\n * Which methods can REALLY complete a payment here, right now.\n *\n * Asked of the SDK, never inferred from a user-agent string: the answer depends\n * on the browser or device, on the buyer's saved cards, AND on whether the\n * merchant account has the rail switched on. A picker listing a wallet the\n * platform cannot use is a dead end with a logo on it.\n *\n * Takes the tender because the answer depends on it — Square binds the total\n * into the wallet it builds, so this is asked at the point of payment with the\n * real amount rather than cached at boot.\n */\n offers(tender: Tender): Promise<Method[]>\n\n /**\n * Draw the rails that render INLINE into the page, and nothing else.\n *\n * Absent on native, and that absence is the honest signal: native card entry\n * is a modal the OS presents, so there is no element to place and nothing for\n * a caller to position. A host writes `terminal.mount?.(…)` and the native\n * build correctly does nothing.\n */\n mount?(method: Method, target: string): Promise<void>\n\n /**\n * Tokenize. Resolves with a token, or rejects.\n *\n * A buyer who dismisses the sheet has not failed — `collect` rejects with a\n * `Cancelled` (see below), which a caller distinguishes from a real failure so\n * it does not show an error for a decision.\n */\n collect(method: Method, tender: Tender, detail?: Detail): Promise<Token>\n\n /** Tear down every element and listener this terminal owns. */\n release(): Promise<void>\n}\n\n/**\n * The buyer dismissed the sheet. NOT an error to show anyone.\n *\n * Every rail signals this differently — Square's web tokenize resolves with\n * `status: 'CANCEL'`, Apple Pay on native calls a cancel callback, Google Pay\n * rejects with its own code — and a caller that cannot tell them apart shows\n * \"payment failed\" to someone who simply changed their mind.\n */\nexport class Cancelled extends Error {\n readonly method: Method\n constructor(method: Method) {\n super(`${method} was cancelled`)\n this.name = 'Cancelled'\n this.method = method\n }\n}\n\n/** True for the one rejection that means \"the buyer said no\", on any platform. */\nexport function cancelled(e: unknown): e is Cancelled {\n return e instanceof Cancelled\n}\n\n/**\n * The rails each platform's Square SDK can even ATTEMPT.\n *\n * This is a fact about the SDKs, not about a merchant account, and the two do not\n * agree: Square's In-App Payments SDK ships no Cash App Pay and no ACH — its\n * whole exported surface is card entry, gift card entry, Apple Pay, Google Pay\n * and buyer verification. Offering either on a phone would be a button that\n * cannot resolve.\n *\n * `offers()` intersects this with what the SDK says it can build, so a rail\n * missing here is absent from the picker rather than present and dead.\n *\n * THIS IS REACH, NOT POLICY, and the difference has already been mistaken once.\n * A host may decline a rail this table lists, for reasons that have nothing to\n * do with tokenizing: hanzoai/pay does not offer ACH on Square, because its\n * top-up endpoint credits the balance as soon as the charge call succeeds while\n * an ACH debit settles days later — so it would credit unsettled money. The SDK\n * reaches ACH perfectly well. Deciding not to use it is the host's call and does\n * not belong here; deleting `ach` below would instead tell every other host that\n * the rail does not exist.\n */\nexport const REACH: Record<'web' | 'native', readonly Method[]> = {\n web: ['card', 'apple_pay', 'google_pay', 'cash_app', 'ach', 'gift'],\n native: ['card', 'apple_pay', 'google_pay', 'gift'],\n}\n","// The card field's colours — the ONE place this package writes a colour down.\n//\n// Everything else on a checkout is styled from the host's own tokens, so a theme\n// change is a class on <html> and no JS is involved. The card fields cannot work\n// that way, and the reason is the same on both platforms for different causes:\n//\n// web Square renders the fields in a CROSS-ORIGIN iframe, which cannot read\n// our custom properties. The only channel is the style object handed to\n// `payments.card({ style })`, and it takes literal values.\n// native Square renders the fields in a NATIVE view. There is no CSS at all;\n// the only channel is `SQIPCardEntry.setIOSCardEntryTheme`, which takes\n// {r,g,b,a} components.\n//\n// So the literals are unavoidable on both. What is avoidable is having them\n// scattered, or drifting from the palette they mirror, or disagreeing between web\n// and native — hence ONE table here, rendered per platform by each backend\n// (`style()` in ./web, `theme()` in ./native). A brand supplies its own table and\n// gets both renderings for free.\n//\n// Every value is an OPAQUE hex. Square validates style values against its own\n// allowlist and rejects what it cannot parse, and an alpha channel over a surface\n// we do not control is a guess about what is behind it anyway. Alpha rungs of a\n// design ladder (white/10, white/40) are therefore composited onto the field's\n// own ground here, which is what the eye sees regardless.\n\n/** The two grounds a checkout can sit on. Dark is the default. */\nexport type Theme = 'dark' | 'light'\n\n// A PALETTE IS ONE SURFACE, and `field` + `text` are the pair that makes it\n// readable. They have forked twice in production, in both directions: once as a\n// white ground under near-white ink (`field: 'transparent'`, so Square's own\n// white showed through), and once as near-black ink placed on this ground on the\n// belief that the ground was still white. Both shipped a card number the customer\n// could not see while typing it. Neither is a judgement call — `legible()` below\n// measures the pair, and the first of those two states measures 1.0 against a 4.5\n// floor. Change a ground and its ink in one edit, and let the check say whether\n// the result can be read.\nexport interface Palette {\n /** The field's own ground. */\n field: string\n /**\n * The hairline — at rest AND on focus. The box deliberately does not move when\n * the buyer arrives in it: `ring` is the one thing that says so, and a border\n * that changed colour underneath it would be a second indicator for the same\n * event. See `ring`.\n */\n border: string\n /**\n * THE focus indicator, drawn AROUND the field rather than on it (`pin()`), so\n * it is the same ring the host draws on its own inputs — one look for every\n * control on the page. Deliberately >= 3:1 on `field` (WCAG 2.4.11).\n */\n ring: string\n /** Typed characters. */\n text: string\n /** Placeholder text. */\n placeholder: string\n /** Validation text, icon and border. One error hue. */\n error: string\n}\n\n// Both columns are @hanzo/design's own values, not new ones. The dark column is\n// the palette `:root` publishes; the light column is what `.light` inverts it to.\n// Composited alphas are noted where the token is a rung rather than a hex.\n//\n// EVERY VALUE HERE IS WHAT A CHECKOUT'S OWN INPUT COMPOSITES TO, measured in a\n// browser rather than reasoned about — because that is the only claim this table\n// can make that matters. `field`/`border` were `--card`/`--white-10-over-card`,\n// which are real tokens for a CARD; an input is not a card, and the strip\n// therefore rendered a lighter ground (#0a0a0a) behind a fainter hairline\n// (#232323) than the fields stacked above it (#000000 / #333333). One row was\n// visibly a different control from its neighbours, which is exactly what a buyer\n// reads as \"this part isn't part of the form\".\nexport const PALETTE: Record<Theme, Palette> = {\n dark: {\n field: '#000000', // --color-black — the ground the checkout column sits on\n border: '#333333', // --ink-20 over that ground (an input's resting edge)\n ring: '#fafafa', // --primary — the page's own focus ring, 19.6:1 on #000000\n text: '#ededed', // --foreground\n placeholder: '#888888', // --muted-foreground\n error: '#fca5a5', // --state-error-text (red-300), legible on black\n },\n light: {\n field: '#ffffff', // --color-black under .light\n border: '#cecece', // --ink-20 over white\n ring: '#0a0a0a', // --primary under .light\n text: '#0a0a0a', // --foreground\n placeholder: '#525252', // --muted-foreground\n error: '#ef4444', // --state-error (red-500), legible on white\n },\n}\n\n/** `#rrggbb`, lowercase. The only form Square accepts on both platforms. */\nexport const HEX = /^#[0-9a-f]{6}$/\n\n/** Relative luminance, WCAG 2.x. */\nexport function luminance(hex: string): number {\n const c = [1, 3, 5].map((i) => {\n const v = parseInt(hex.slice(i, i + 2), 16) / 255\n return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4\n })\n return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]\n}\n\n/** Contrast ratio between two opaque hexes, 1..21. Order does not matter. */\nexport function ratio(a: string, b: string): number {\n const [x, y] = [luminance(a), luminance(b)].sort((m, n) => n - m)\n return (x + 0.05) / (y + 0.05)\n}\n\n/** One failed contrast pair: what was measured, against what floor. */\nexport interface Fault {\n pair: string\n ratio: number\n floor: number\n}\n\n/**\n * Every contrast pair in a palette that a customer must be able to read, with\n * the WCAG floor each is held to.\n *\n * This is exported rather than kept in a test because a BRAND supplying its own\n * table needs the same check, and the failure it catches is invisible in review:\n * a reviewer looking at a dark mock cannot tell 2.6:1 from 4.4:1, and the focus\n * ring is a WCAG 2.4.11 requirement rather than a preference.\n */\nexport function legible(p: Palette): Fault[] {\n const pairs: Array<[string, string, string, number]> = [\n // The typed card number, at AA for body text.\n ['text on field', p.text, p.field, 4.5],\n // A placeholder that is merely present, not legible, is a label nobody reads.\n ['placeholder on field', p.placeholder, p.field, 4.5],\n // Non-text contrast: this IS the focus indicator (WCAG 2.4.11).\n ['ring on field', p.ring, p.field, 3],\n // The error hue carries both text and a border, so it is held to the lower\n // non-text floor — it is never the only signal, the border moves too.\n ['error on field', p.error, p.field, 3],\n ]\n return pairs\n .map(([pair, a, b, floor]) => ({ pair, ratio: ratio(a, b), floor }))\n .filter((f) => f.ratio < f.floor)\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/terminal.ts","../src/palette.ts"],"names":[],"mappings":"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","file":"chunk-IYAU5BMM.js","sourcesContent":["// What a checkout asks for, what it gets back, and the one thing that turns the\n// first into the second. Platform-agnostic: no DOM, no react-native, no Square.\n//\n// THIS PACKAGE NEVER MOVES MONEY. Every rail below ends at a single-use token,\n// and the charge is a server call the host makes with that token. Nothing here\n// completes a payment, which is why the noun is `Tender` — an OFFER of payment,\n// the term Square's own Orders API uses — and not `Charge`, which would name\n// something this code cannot do.\n\n/**\n * A Square rail.\n *\n * `gift` is a Square gift card, which tokenizes like a card but through its own\n * constructor on both platforms. Non-Square rails a host may also offer (wire,\n * crypto, another processor) are deliberately absent: this package knows Square,\n * and a host that knows more merges its own set with what `offers()` returns.\n */\nexport type Method = 'card' | 'apple_pay' | 'google_pay' | 'cash_app' | 'ach' | 'gift'\n\n/**\n * Money as a DECIMAL STRING, never a number.\n *\n * `0.1 + 0.2` is `0.30000000000000004`, and a float that reaches a payment\n * processor as a total is a rounding error someone is charged. Square's own\n * paymentRequest takes `amount` as a string for this reason; so does this.\n */\nexport interface Money {\n /** Decimal string in major units, e.g. `\"12.00\"`. Never a float. */\n amount: string\n /** ISO 4217, e.g. `\"USD\"`. */\n currency: string\n}\n\n/** An offer of payment: what is being paid, where, and what the buyer is shown. */\nexport interface Tender {\n total: Money\n /** ISO 3166-1 alpha-2 of the MERCHANT, e.g. `\"US\"`. Square keys wallets off it. */\n country: string\n /** The line the wallet sheet shows the buyer, e.g. `\"Hanzo AI credit\"`. */\n label: string\n}\n\n/**\n * A single-use payment token, and the only thing any rail here produces.\n *\n * It is single-use in the strict sense: spending it — charging it OR vaulting it\n * — consumes it. A flow that needs both a charge and a card-on-file must\n * tokenize TWICE; sharing one token between the two fails with the card already\n * accepted, which reads as a decline.\n */\nexport interface Token {\n value: string\n method: Method\n /** Present when the rail reported it. Absent means never reported, not empty. */\n card?: { brand?: string; last4?: string; expMonth?: number; expYear?: number }\n}\n\n/**\n * Rail-specific facts a `Tender` cannot carry, because only one rail needs each.\n * Every field is ignored by the rails it does not belong to.\n */\nexport interface Detail {\n /**\n * ACH only (web), and REQUIRED there: Square matches it against the bank\n * record. Collected from the buyer rather than derived from the account,\n * because the account holder and the person paying are not always the same\n * name.\n */\n name?: string\n /**\n * Card and gift only. Passed straight to Square as verification details —\n * `{ intent, amount, currencyCode, billingContact, customerInitiated,\n * sellerKeyedIn }` — which is how SCA is satisfied in mandated regions.\n * Opaque on purpose: Square owns this shape, and re-declaring it here would\n * be a second copy to drift.\n */\n verify?: Record<string, unknown>\n /**\n * NATIVE ONLY, and the one real asymmetry between the platforms.\n *\n * Square's native card sheet is a state machine, not a form: it STAYS OPEN\n * after minting the token, showing a spinner, until it is told whether the\n * charge worked. Told yes, it closes. Told no, it shows the message ON the\n * sheet with the card still entered, so the buyer retries in place instead of\n * being dropped back to a checkout with an empty form and a decline notice.\n *\n * That is a better decline experience than the web can offer, and it exists\n * only if the host charges DURING the sheet — which is what this is. Given the\n * token, run the server call; return to close the sheet, throw to keep it open\n * with the thrown message shown.\n *\n * Omit it and the sheet closes as soon as the token exists, which is what the\n * web does. Nothing hangs either way.\n *\n * The web backend ignores this: its card fields are a page element with no\n * sheet to hold open.\n */\n charge?(token: Token): Promise<void>\n}\n\n/**\n * The one interface both backends satisfy: web (Square Web Payments SDK) and\n * native (Square In-App Payments SDK). A host writes against this and the\n * platform picks the implementation — see `./web` and `./native`.\n */\nexport interface Terminal {\n /**\n * Which methods can REALLY complete a payment here, right now.\n *\n * Asked of the SDK, never inferred from a user-agent string: the answer depends\n * on the browser or device, on the buyer's saved cards, AND on whether the\n * merchant account has the rail switched on. A picker listing a wallet the\n * platform cannot use is a dead end with a logo on it.\n *\n * Takes the tender because the answer depends on it — Square binds the total\n * into the wallet it builds, so this is asked at the point of payment with the\n * real amount rather than cached at boot.\n */\n offers(tender: Tender): Promise<Method[]>\n\n /**\n * Draw the rails that render INLINE into the page, and nothing else.\n *\n * Absent on native, and that absence is the honest signal: native card entry\n * is a modal the OS presents, so there is no element to place and nothing for\n * a caller to position. A host writes `terminal.mount?.(…)` and the native\n * build correctly does nothing.\n */\n mount?(method: Method, target: string): Promise<void>\n\n /**\n * Tokenize. Resolves with a token, or rejects.\n *\n * A buyer who dismisses the sheet has not failed — `collect` rejects with a\n * `Cancelled` (see below), which a caller distinguishes from a real failure so\n * it does not show an error for a decision.\n */\n collect(method: Method, tender: Tender, detail?: Detail): Promise<Token>\n\n /** Tear down every element and listener this terminal owns. */\n release(): Promise<void>\n}\n\n/**\n * The buyer dismissed the sheet. NOT an error to show anyone.\n *\n * Every rail signals this differently — Square's web tokenize resolves with\n * `status: 'CANCEL'`, Apple Pay on native calls a cancel callback, Google Pay\n * rejects with its own code — and a caller that cannot tell them apart shows\n * \"payment failed\" to someone who simply changed their mind.\n */\nexport class Cancelled extends Error {\n readonly method: Method\n constructor(method: Method) {\n super(`${method} was cancelled`)\n this.name = 'Cancelled'\n this.method = method\n }\n}\n\n/** True for the one rejection that means \"the buyer said no\", on any platform. */\nexport function cancelled(e: unknown): e is Cancelled {\n return e instanceof Cancelled\n}\n\n/**\n * The rails each platform's Square SDK can even ATTEMPT.\n *\n * This is a fact about the SDKs, not about a merchant account, and the two do not\n * agree: Square's In-App Payments SDK ships no Cash App Pay and no ACH — its\n * whole exported surface is card entry, gift card entry, Apple Pay, Google Pay\n * and buyer verification. Offering either on a phone would be a button that\n * cannot resolve.\n *\n * `offers()` intersects this with what the SDK says it can build, so a rail\n * missing here is absent from the picker rather than present and dead.\n *\n * THIS IS REACH, NOT POLICY, and the difference has already been mistaken once.\n * A host may decline a rail this table lists, for reasons that have nothing to\n * do with tokenizing: hanzoai/pay does not offer ACH on Square, because its\n * top-up endpoint credits the balance as soon as the charge call succeeds while\n * an ACH debit settles days later — so it would credit unsettled money. The SDK\n * reaches ACH perfectly well. Deciding not to use it is the host's call and does\n * not belong here; deleting `ach` below would instead tell every other host that\n * the rail does not exist.\n */\nexport const REACH: Record<'web' | 'native', readonly Method[]> = {\n web: ['card', 'apple_pay', 'google_pay', 'cash_app', 'ach', 'gift'],\n native: ['card', 'apple_pay', 'google_pay', 'gift'],\n}\n","// The card field's colours — the ONE place this package writes a colour down.\n//\n// Everything else on a checkout is styled from the host's own tokens, so a theme\n// change is a class on <html> and no JS is involved. The card fields cannot work\n// that way, and the reason is the same on both platforms for different causes:\n//\n// web Square renders the fields in a CROSS-ORIGIN iframe, which cannot read\n// our custom properties. The only channel is the style object handed to\n// `payments.card({ style })`, and it takes literal values.\n// native Square renders the fields in a NATIVE view. There is no CSS at all;\n// the only channel is `SQIPCardEntry.setIOSCardEntryTheme`, which takes\n// {r,g,b,a} components.\n//\n// So the literals are unavoidable on both. What is avoidable is having them\n// scattered, or drifting from the palette they mirror, or disagreeing between web\n// and native — hence ONE table here, rendered per platform by each backend\n// (`style()` in ./web, `theme()` in ./native). A brand supplies its own table and\n// gets both renderings for free.\n//\n// Every value is an OPAQUE hex. Square validates style values against its own\n// allowlist and rejects what it cannot parse, and an alpha channel over a surface\n// we do not control is a guess about what is behind it anyway. Alpha rungs of a\n// design ladder (white/10, white/40) are therefore composited onto the field's\n// own ground here, which is what the eye sees regardless.\n\n/** The two grounds a checkout can sit on. Dark is the default. */\nexport type Theme = 'dark' | 'light'\n\n// A PALETTE IS ONE SURFACE, and `field` + `text` are the pair that makes it\n// readable. They have forked twice in production, in both directions: once as a\n// white ground under near-white ink (`field: 'transparent'`, so Square's own\n// white showed through), and once as near-black ink placed on this ground on the\n// belief that the ground was still white. Both shipped a card number the customer\n// could not see while typing it. Neither is a judgement call — `legible()` below\n// measures the pair, and the first of those two states measures 1.0 against a 4.5\n// floor. Change a ground and its ink in one edit, and let the check say whether\n// the result can be read.\nexport interface Palette {\n /** The field's own ground. */\n field: string\n /**\n * The hairline — at rest AND on focus. The box deliberately does not move when\n * the buyer arrives in it: `ring` is the one thing that says so, and a border\n * that changed colour underneath it would be a second indicator for the same\n * event. See `ring`.\n */\n border: string\n /**\n * THE focus indicator, drawn AROUND the field rather than on it (`pin()`), so\n * it is the same ring the host draws on its own inputs — one look for every\n * control on the page. Deliberately >= 3:1 on `field` (WCAG 2.4.11).\n */\n ring: string\n /** Typed characters. */\n text: string\n /** Placeholder text. */\n placeholder: string\n /** Validation text, icon and border. One error hue. */\n error: string\n}\n\n// Both columns are @hanzo/design's own values, not new ones. The dark column is\n// the palette `:root` publishes; the light column is what `.light` inverts it to.\n// Composited alphas are noted where the token is a rung rather than a hex.\n//\n// EVERY VALUE HERE IS WHAT A CHECKOUT'S OWN INPUT COMPOSITES TO, measured in a\n// browser rather than reasoned about — because that is the only claim this table\n// can make that matters. `field`/`border` were `--card`/`--white-10-over-card`,\n// which are real tokens for a CARD; an input is not a card, and the strip\n// therefore rendered a lighter ground (#0a0a0a) behind a fainter hairline\n// (#232323) than the fields stacked above it (#000000 / #333333). One row was\n// visibly a different control from its neighbours, which is exactly what a buyer\n// reads as \"this part isn't part of the form\".\nexport const PALETTE: Record<Theme, Palette> = {\n dark: {\n field: '#000000', // --color-black — the ground the checkout column sits on\n border: '#333333', // --ink-20 over that ground (an input's resting edge)\n ring: '#fafafa', // --primary — the page's own focus ring, 19.6:1 on #000000\n text: '#ededed', // --foreground\n placeholder: '#888888', // --muted-foreground\n error: '#fca5a5', // --state-error-text (red-300), legible on black\n },\n light: {\n field: '#ffffff', // --color-black under .light\n border: '#cecece', // --ink-20 over white\n ring: '#0a0a0a', // --primary under .light\n text: '#0a0a0a', // --foreground\n placeholder: '#525252', // --muted-foreground\n error: '#ef4444', // --state-error (red-500), legible on white\n },\n}\n\n/** `#rrggbb`, lowercase. The only form Square accepts on both platforms. */\nexport const HEX = /^#[0-9a-f]{6}$/\n\n/** Relative luminance, WCAG 2.x. */\nexport function luminance(hex: string): number {\n const c = [1, 3, 5].map((i) => {\n const v = parseInt(hex.slice(i, i + 2), 16) / 255\n return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4\n })\n return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]\n}\n\n/** Contrast ratio between two opaque hexes, 1..21. Order does not matter. */\nexport function ratio(a: string, b: string): number {\n const [x, y] = [luminance(a), luminance(b)].sort((m, n) => n - m)\n return (x + 0.05) / (y + 0.05)\n}\n\n/** One failed contrast pair: what was measured, against what floor. */\nexport interface Fault {\n pair: string\n ratio: number\n floor: number\n}\n\n/**\n * Every contrast pair in a palette that a customer must be able to read, with\n * the WCAG floor each is held to.\n *\n * This is exported rather than kept in a test because a BRAND supplying its own\n * table needs the same check, and the failure it catches is invisible in review:\n * a reviewer looking at a dark mock cannot tell 2.6:1 from 4.4:1, and the focus\n * ring is a WCAG 2.4.11 requirement rather than a preference.\n */\nexport function legible(p: Palette): Fault[] {\n const pairs: Array<[string, string, string, number]> = [\n // The typed card number, at AA for body text.\n ['text on field', p.text, p.field, 4.5],\n // A placeholder that is merely present, not legible, is a label nobody reads.\n ['placeholder on field', p.placeholder, p.field, 4.5],\n // Non-text contrast: this IS the focus indicator (WCAG 2.4.11).\n ['ring on field', p.ring, p.field, 3],\n // The error hue carries both text and a border, so it is held to the lower\n // non-text floor — it is never the only signal, the border moves too.\n ['error on field', p.error, p.field, 3],\n ]\n return pairs\n .map(([pair, a, b, floor]) => ({ pair, ratio: ratio(a, b), floor }))\n .filter((f) => f.ratio < f.floor)\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/attempt.ts"],"names":[],"mappings":";;;;;AAkBO,IAAM,KAAA,GAAQ;AAUd,IAAM,KAAA,GAAQ;AAWd,IAAM,OAAA,GAAU;AAqBhB,SAAS,OAAA,CAAQ,WAAW,EAAA,EAAY;AAC7C,EAAA,MAAM,SAAS,KAAA,GAAQ,QAAA;AACvB,EAAA,IAAI,SAAS,KAAA,EAAO;AAClB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,EAAA,EAAK,QAAQ,CAAA,0BAAA,EAA6B,MAAM,+BAA+B,KAAK,CAAA,SAAA;AAAA,KACtF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,OAAO,CAAA;AACrC,EAAA,MAAM,OAAO,UAAA,CAAW,MAAA,EAAQ,cAAa,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA;AAC/D,EAAA,IAAI,IAAA,EAAM,OAAO,IAAA,CAAK,KAAA,CAAM,GAAG,IAAI,CAAA;AAKnC,EAAA,IAAI,GAAA,GAAM,IAAA,CAAK,GAAA,EAAI,CAAE,SAAS,EAAE,CAAA;AAChC,EAAA,OAAO,GAAA,CAAI,MAAA,GAAS,OAAA,EAAS,GAAA,IAAO,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA;AACtE,EAAA,OAAO,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,IAAI,CAAA;AAC1B;AAUO,SAAS,KAAA,CAAM,GAAA,EAAa,QAAA,GAAW,EAAA,EAAY;AACxD,EAAA,OAAO,GAAA,CAAI,MAAM,CAAA,EAAG,IAAA,CAAK,IAAI,KAAA,EAAO,KAAA,GAAQ,QAAQ,CAAC,CAAA;AACvD","file":"index.cjs","sourcesContent":["// The idempotency key, and the ruler it answers to.\n//\n// This package never charges anything, so it never sends this header. It mints\n// the key anyway, because the CONSTRAINT is a Square protocol fact and Square\n// protocol facts are what this package exists to hold — and because the shape of\n// the failure is one no caller can debug from where it lands.\n//\n// A key over 45 characters is rejected by Square with VALUE_TOO_LONG, BEFORE a\n// cent moves. hanzoai/pay minted a 36-char UUID and commerce forwarded it\n// wrapped as `<op>:<org>:<key>`, so `topup:hanzo:` + 36 = 49 and EVERY card\n// charge for that org failed. Nothing about that surfaces as \"the key is too\n// long\": the client sent a perfectly ordinary UUID, the failure arrived from a\n// processor two hops away, and it read as \"charge failed\".\n\n/**\n * Square's ceiling on `idempotency_key`, in characters. Not a guideline — it is\n * a hard rejection, and it happens before authorization.\n */\nexport const LIMIT = 45\n\n/**\n * The smallest key worth minting.\n *\n * Keys are namespaced per org by the wrapper, so a collision inside one org\n * would replay ANOTHER buyer's charge — the one failure mode worse than\n * double-charging. Below this there is not enough entropy to rule that out, and\n * silently minting a short key would trade a loud error for a quiet one.\n */\nexport const FLOOR = 8\n\n/**\n * The largest key worth minting: a whole UUID with its hyphens removed.\n *\n * 128 bits already rules out collision by any margin that matters, so a budget\n * bigger than this buys nothing. Naming the ceiling is what stops `attempt()`\n * quietly returning fewer characters than it was asked for — which it did, and\n * which its own test caught: 45 minus a 10-character wrapper is 35, and a\n * stripped UUID is 32.\n */\nexport const CEILING = 32\n\n/**\n * A key identifying ONE payment attempt, sized to survive being wrapped.\n *\n * Hold it across retries — that is the entire point. A retry carrying the same\n * key replays the first result instead of charging again, even a retry that had\n * to re-tokenize a fresh single-use token because the buyer pressed \"start\n * over\". A key minted per REQUEST rather than per ATTEMPT provides no protection\n * whatsoever, and looks identical from here.\n *\n * `reserved` is how many characters the server will prepend. The default 25 is\n * commerce's worst case and yields the 20-char key hanzoai/pay ships:\n * `subscribe:` (10) + an org slug + `:` (1), which leaves room for slugs up to\n * 14 even under the longest op. A host whose wrapper is shorter may pass a\n * smaller number and get a longer key; none of them needs a longer key.\n *\n * Entropy comes from `crypto.randomUUID` where it exists. The fallback is time\n * plus `Math.random`, which is weaker and deliberately not silent about it —\n * see the note there.\n */\nexport function attempt(reserved = 25): string {\n const budget = LIMIT - reserved\n if (budget < FLOOR) {\n throw new Error(\n `a ${reserved}-character wrapper leaves ${budget} for the key; Square allows ${LIMIT} in total`,\n )\n }\n const size = Math.min(budget, CEILING)\n const uuid = globalThis.crypto?.randomUUID?.().replace(/-/g, '')\n if (uuid) return uuid.slice(0, size)\n // No crypto: an older RN runtime without `react-native-get-random-values`, or\n // a non-secure browser context. Draw until the ceiling is covered, because ONE\n // `Math.random().toString(36)` is ~11 characters — a key that is short where\n // it looks long is the failure this whole module is about.\n let raw = Date.now().toString(36)\n while (raw.length < CEILING) raw += Math.random().toString(36).slice(2)\n return raw.slice(0, size)\n}\n\n/**\n * Cut a key that was minted before anyone knew about the ceiling.\n *\n * Shortening a STORED key cannot skip a replay, and that is what makes this safe\n * rather than reckless: Square never accepted the long key, so no charge exists\n * under it to replay. A session holding a 36-character UUID would otherwise\n * re-fail forever, having carefully preserved the exact value that cannot work.\n */\nexport function clamp(key: string, reserved = 25): string {\n return key.slice(0, Math.max(FLOOR, LIMIT - reserved))\n}\n"]}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/attempt.ts"],"names":[],"mappings":";;;AAkBO,IAAM,KAAA,GAAQ;AAUd,IAAM,KAAA,GAAQ;AAWd,IAAM,OAAA,GAAU;AAqBhB,SAAS,OAAA,CAAQ,WAAW,EAAA,EAAY;AAC7C,EAAA,MAAM,SAAS,KAAA,GAAQ,QAAA;AACvB,EAAA,IAAI,SAAS,KAAA,EAAO;AAClB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,EAAA,EAAK,QAAQ,CAAA,0BAAA,EAA6B,MAAM,+BAA+B,KAAK,CAAA,SAAA;AAAA,KACtF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,OAAO,CAAA;AACrC,EAAA,MAAM,OAAO,UAAA,CAAW,MAAA,EAAQ,cAAa,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA;AAC/D,EAAA,IAAI,IAAA,EAAM,OAAO,IAAA,CAAK,KAAA,CAAM,GAAG,IAAI,CAAA;AAKnC,EAAA,IAAI,GAAA,GAAM,IAAA,CAAK,GAAA,EAAI,CAAE,SAAS,EAAE,CAAA;AAChC,EAAA,OAAO,GAAA,CAAI,MAAA,GAAS,OAAA,EAAS,GAAA,IAAO,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA;AACtE,EAAA,OAAO,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,IAAI,CAAA;AAC1B;AAUO,SAAS,KAAA,CAAM,GAAA,EAAa,QAAA,GAAW,EAAA,EAAY;AACxD,EAAA,OAAO,GAAA,CAAI,MAAM,CAAA,EAAG,IAAA,CAAK,IAAI,KAAA,EAAO,KAAA,GAAQ,QAAQ,CAAC,CAAA;AACvD","file":"index.js","sourcesContent":["// The idempotency key, and the ruler it answers to.\n//\n// This package never charges anything, so it never sends this header. It mints\n// the key anyway, because the CONSTRAINT is a Square protocol fact and Square\n// protocol facts are what this package exists to hold — and because the shape of\n// the failure is one no caller can debug from where it lands.\n//\n// A key over 45 characters is rejected by Square with VALUE_TOO_LONG, BEFORE a\n// cent moves. hanzoai/pay minted a 36-char UUID and commerce forwarded it\n// wrapped as `<op>:<org>:<key>`, so `topup:hanzo:` + 36 = 49 and EVERY card\n// charge for that org failed. Nothing about that surfaces as \"the key is too\n// long\": the client sent a perfectly ordinary UUID, the failure arrived from a\n// processor two hops away, and it read as \"charge failed\".\n\n/**\n * Square's ceiling on `idempotency_key`, in characters. Not a guideline — it is\n * a hard rejection, and it happens before authorization.\n */\nexport const LIMIT = 45\n\n/**\n * The smallest key worth minting.\n *\n * Keys are namespaced per org by the wrapper, so a collision inside one org\n * would replay ANOTHER buyer's charge — the one failure mode worse than\n * double-charging. Below this there is not enough entropy to rule that out, and\n * silently minting a short key would trade a loud error for a quiet one.\n */\nexport const FLOOR = 8\n\n/**\n * The largest key worth minting: a whole UUID with its hyphens removed.\n *\n * 128 bits already rules out collision by any margin that matters, so a budget\n * bigger than this buys nothing. Naming the ceiling is what stops `attempt()`\n * quietly returning fewer characters than it was asked for — which it did, and\n * which its own test caught: 45 minus a 10-character wrapper is 35, and a\n * stripped UUID is 32.\n */\nexport const CEILING = 32\n\n/**\n * A key identifying ONE payment attempt, sized to survive being wrapped.\n *\n * Hold it across retries — that is the entire point. A retry carrying the same\n * key replays the first result instead of charging again, even a retry that had\n * to re-tokenize a fresh single-use token because the buyer pressed \"start\n * over\". A key minted per REQUEST rather than per ATTEMPT provides no protection\n * whatsoever, and looks identical from here.\n *\n * `reserved` is how many characters the server will prepend. The default 25 is\n * commerce's worst case and yields the 20-char key hanzoai/pay ships:\n * `subscribe:` (10) + an org slug + `:` (1), which leaves room for slugs up to\n * 14 even under the longest op. A host whose wrapper is shorter may pass a\n * smaller number and get a longer key; none of them needs a longer key.\n *\n * Entropy comes from `crypto.randomUUID` where it exists. The fallback is time\n * plus `Math.random`, which is weaker and deliberately not silent about it —\n * see the note there.\n */\nexport function attempt(reserved = 25): string {\n const budget = LIMIT - reserved\n if (budget < FLOOR) {\n throw new Error(\n `a ${reserved}-character wrapper leaves ${budget} for the key; Square allows ${LIMIT} in total`,\n )\n }\n const size = Math.min(budget, CEILING)\n const uuid = globalThis.crypto?.randomUUID?.().replace(/-/g, '')\n if (uuid) return uuid.slice(0, size)\n // No crypto: an older RN runtime without `react-native-get-random-values`, or\n // a non-secure browser context. Draw until the ceiling is covered, because ONE\n // `Math.random().toString(36)` is ~11 characters — a key that is short where\n // it looks long is the failure this whole module is about.\n let raw = Date.now().toString(36)\n while (raw.length < CEILING) raw += Math.random().toString(36).slice(2)\n return raw.slice(0, size)\n}\n\n/**\n * Cut a key that was minted before anyone knew about the ceiling.\n *\n * Shortening a STORED key cannot skip a replay, and that is what makes this safe\n * rather than reckless: Square never accepted the long key, so no charge exists\n * under it to replay. A session holding a 36-character UUID would otherwise\n * re-fail forever, having carefully preserved the exact value that cannot work.\n */\nexport function clamp(key: string, reserved = 25): string {\n return key.slice(0, Math.max(FLOOR, LIMIT - reserved))\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/native/theme.ts","../../src/native/terminal.ts"],"names":["Brand","SQIPApplePay","SQIPGooglePay","REACH","Cancelled","SQIPCardEntry","ApplePayNonceSuccessState","GooglePayPriceStatus","SQIPCore","PALETTE","GooglePayEnvironment"],"mappings":"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","file":"index.cjs","sourcesContent":["// A `Palette` rendered as the In-App Payments SDK's theme.\n//\n// Same table as the web backend paints the iframe with, so a card field looks\n// the same on a phone as it does in a browser without anyone maintaining two\n// sets of colours. The SDK takes {r,g,b,a} components rather than CSS, which is\n// the entire difference.\n\nimport type { Palette, Theme } from '../palette'\n\n/** The SDK's colour, 0-255 per channel with an optional 0-1 alpha. */\nexport interface Colour {\n r: number\n g: number\n b: number\n a?: number\n}\n\n/**\n * `SQIPCardEntry.setIOSCardEntryTheme`'s argument, as much of it as a palette can\n * answer for. Structurally assignable to the plugin's own `ThemeType`.\n */\nexport interface Appearance {\n backgroundColor?: Colour\n textColor?: Colour\n placeholderTextColor?: Colour\n tintColor?: Colour\n messageColor?: Colour\n errorColor?: Colour\n saveButtonTextColor?: Colour\n keyboardAppearance?: 'Light' | 'Dark'\n}\n\n/** `#rrggbb` -> components. Throws rather than guessing: a silent 0,0,0 is black. */\nexport function colour(hex: string): Colour {\n if (!/^#[0-9a-fA-F]{6}$/.test(hex)) throw new Error(`not an opaque hex colour: ${hex}`)\n return {\n r: parseInt(hex.slice(1, 3), 16),\n g: parseInt(hex.slice(3, 5), 16),\n b: parseInt(hex.slice(5, 7), 16),\n a: 1,\n }\n}\n\n/**\n * The native card sheet's appearance for `p`.\n *\n * TWO HONEST GAPS, both properties of the SDK rather than of this mapping:\n *\n * `border` has no counterpart. iOS draws its own field separators and exposes\n * no colour for them, so the palette's hairline is simply unused here. It is\n * not dropped by oversight and must not be mapped onto something else that\n * happens to accept a colour.\n *\n * THIS IS iOS ONLY. The setter is named for the platform it configures, and\n * Android's card entry is themed through native XML styles at build time —\n * there is no runtime channel to it at all. An Android build gets Square's\n * default sheet unless the app ships the corresponding style resource.\n *\n * `keyboardAppearance` is included because it is the one that gets noticed: a\n * light keyboard sliding up under a black sheet is the most visible seam on the\n * screen, and it is a single word to get right.\n */\nexport function appearance(p: Palette, theme: Theme): Appearance {\n return {\n backgroundColor: colour(p.field),\n textColor: colour(p.text),\n placeholderTextColor: colour(p.placeholder),\n // The caret and the active field's accent — the native rendering of the same\n // focus colour the web backend draws a ring in, and the same value for the\n // same reason.\n tintColor: colour(p.ring),\n messageColor: colour(p.placeholder),\n errorColor: colour(p.error),\n saveButtonTextColor: colour(p.text),\n keyboardAppearance: theme === 'dark' ? 'Dark' : 'Light',\n }\n}\n","// Square's In-App Payments SDK behind the same `Terminal` interface the web\n// backend satisfies.\n//\n// The two SDKs are not parallel and this file is where that stops being the\n// host's problem:\n//\n// The web SDK hands back objects you attach and tokenize. This one exposes\n// NAMESPACES OF CALLBACK FUNCTIONS that present native UI — so every rail here\n// is a callback trio (success / failure / cancel) wrapped into one promise.\n//\n// There is nothing to mount. Card entry is a sheet the OS presents, so\n// `mount` is absent rather than a no-op, which is the honest signal to a host\n// that there is no element to position.\n//\n// Card entry is a STATE MACHINE, not a form: the sheet stays open after\n// minting the token and is told, afterwards, whether the charge worked. See\n// `Detail.charge`.\n//\n// Cash App Pay and ACH are absent from `REACH.native` because this SDK has no\n// such rail — not because they are unfinished.\n\nimport {\n SQIPApplePay,\n SQIPCardEntry,\n SQIPCore,\n SQIPGooglePay,\n Brand,\n GooglePayEnvironment,\n GooglePayPriceStatus,\n ApplePayNonceSuccessState,\n type ApplePayNonceSuccessResult,\n type CardDetails,\n type ErrorDetails,\n type NonceSuccessResult,\n} from 'react-native-square-in-app-payments'\n\nimport {\n type Detail,\n type Method,\n type Tender,\n type Terminal,\n type Token,\n Cancelled,\n REACH,\n} from '../terminal'\nimport { type Palette, PALETTE, type Theme } from '../palette'\nimport { appearance } from './theme'\n\nexport interface Config {\n applicationId: string\n locationId: string\n /** `'sandbox'` tokenizes against Square's sandbox. Anything else is production. */\n environment?: string\n /**\n * The Apple merchant id (`merchant.com.example`) from the app's entitlements.\n * Apple Pay is simply not offered without one — there is nothing to fall back\n * to, and a button that cannot open a sheet is worse than no button.\n */\n appleMerchantId?: string\n /** The card sheet's colours. Defaults to @hanzo/design's dark table. */\n palette?: Palette\n /** Chooses the keyboard's own appearance under the sheet. Defaults to dark. */\n theme?: Theme\n}\n\n/** The plugin's numeric brand enum, as the string the web SDK already reports. */\nconst BRAND: Partial<Record<Brand, string>> = {\n [Brand.VISA]: 'VISA',\n [Brand.MASTERCARD]: 'MASTERCARD',\n [Brand.AMERICAN_EXPRESS]: 'AMERICAN_EXPRESS',\n [Brand.DISCOVER]: 'DISCOVER',\n [Brand.DISCOVER_DINERS]: 'DISCOVER_DINERS',\n [Brand.JCB]: 'JCB',\n [Brand.CHINA_UNION_PAY]: 'CHINA_UNION_PAY',\n [Brand.SQUARE_GIFT_CARD]: 'SQUARE_GIFT_CARD',\n}\n\nfunction token(method: Method, d: CardDetails): Token {\n if (!d.nonce) throw new Error(`${method} returned no token`)\n return {\n value: d.nonce,\n method,\n card: d.card\n ? {\n brand: d.card.brand === undefined ? undefined : BRAND[d.card.brand],\n last4: d.card.lastFourDigits,\n expMonth: d.card.expirationMonth,\n expYear: d.card.expirationYear,\n }\n : undefined,\n }\n}\n\nclass Native implements Terminal {\n constructor(private readonly config: Config) {}\n\n /**\n * What this DEVICE can pay with.\n *\n * Card and gift entry are always available — they are Square's own sheets and\n * need no wallet, no saved card and no capability beyond the SDK being linked.\n * The two wallets are asked, because both can be missing for reasons no\n * user-agent string would reveal: no card in Wallet, no Play Services, a\n * merchant id the app was not signed for.\n *\n * The tender is unused here. Native availability does not depend on the\n * amount, unlike the web SDK, where the total is bound into the wallet object\n * at construction — the interface carries it for the platform that needs it.\n */\n async offers(_tender: Tender): Promise<Method[]> {\n const out: Method[] = ['card', 'gift']\n if (this.config.appleMerchantId) {\n try {\n if (await SQIPApplePay.canUseApplePay()) out.push('apple_pay')\n } catch {\n // Not offerable on this device. Say nothing, show nothing.\n }\n }\n try {\n if (await SQIPGooglePay.canUseGooglePay()) out.push('google_pay')\n } catch {\n // Likewise.\n }\n return out.filter((m) => REACH.native.includes(m))\n }\n\n collect(method: Method, tender: Tender, detail?: Detail): Promise<Token> {\n if (!REACH.native.includes(method)) {\n return Promise.reject(new Error(`${method} is not a rail this SDK has`))\n }\n if (method === 'card' || method === 'gift') return this.sheet(method, detail)\n if (method === 'apple_pay') return this.apple(tender, detail)\n return this.google(tender, detail)\n }\n\n /**\n * Card and gift entry: one sheet, held open until the charge is known.\n *\n * The promise deliberately stays PENDING while the buyer retries. A declined\n * card returns `{ success: false }`, which keeps Square's sheet up with the\n * message shown and the card still entered — resolving or rejecting here would\n * settle a payment the buyer is still in the middle of making.\n */\n private sheet(method: 'card' | 'gift', detail?: Detail): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n const onNonce = async (d: CardDetails): Promise<NonceSuccessResult> => {\n let t: Token\n try {\n t = token(method, d)\n } catch (e) {\n return { success: false, errorMessage: message(e) }\n }\n if (detail?.charge) {\n try {\n await detail.charge(t)\n } catch (e) {\n // Stay on the sheet so the buyer can correct the card in place.\n return { success: false, errorMessage: message(e) }\n }\n }\n // `onCardEntryComplete` fires once the sheet has actually closed, so the\n // host's next screen never renders behind a dismissing modal.\n return { success: true, onCardEntryComplete: () => resolve(t) }\n }\n const onCancel = () => reject(new Cancelled(method))\n if (method === 'gift') SQIPCardEntry.startGiftCardEntryFlow(onNonce, onCancel)\n // `false` collects no postal code: the billing address belongs to the\n // payer and the host's own checkout already owns it, so asking again\n // inside the sheet is a second answer to one question.\n else SQIPCardEntry.startCardEntryFlow(false, onNonce, onCancel)\n })\n }\n\n private apple(tender: Tender, detail?: Detail): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n let settled: Token | null = null\n void SQIPApplePay.requestApplePayNonce(\n {\n price: tender.total.amount,\n summaryLabel: tender.label,\n countryCode: tender.country,\n currencyCode: tender.total.currency,\n },\n async (d: CardDetails): Promise<ApplePayNonceSuccessResult> => {\n let t: Token\n try {\n t = token('apple_pay', d)\n } catch (e) {\n return { state: ApplePayNonceSuccessState.Failure, errorMessage: message(e) }\n }\n if (detail?.charge) {\n try {\n await detail.charge(t)\n } catch (e) {\n // Apple's sheet shows its own failure state and then closes; there\n // is no retry-in-place here the way there is for card entry.\n return { state: ApplePayNonceSuccessState.Failure, errorMessage: message(e) }\n }\n }\n settled = t\n return { state: ApplePayNonceSuccessState.Succeeded }\n },\n (e: ErrorDetails) => reject(new Error(e.message)),\n // The completion callback is the ONLY place that knows how the sheet\n // ended, so it is the one that settles the promise.\n //\n // Annotated because `requestApplePayNonce` is overloaded and its\n // DEPRECATED signature takes a bare `() => void` here — TypeScript picks\n // that one and infers nothing, so the two arguments this needs would\n // silently be `any`.\n (state: ApplePayNonceSuccessState, errorMessage?: string) => {\n if (state === ApplePayNonceSuccessState.Succeeded && settled) resolve(settled)\n else if (state === ApplePayNonceSuccessState.Canceled) reject(new Cancelled('apple_pay'))\n else reject(new Error(errorMessage || 'Apple Pay could not be completed'))\n },\n )\n })\n }\n\n private google(tender: Tender, detail?: Detail): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n void SQIPGooglePay.requestGooglePayNonce(\n {\n price: tender.total.amount,\n currencyCode: tender.total.currency,\n // FINAL, because this total is what the host will charge. Quoting it\n // as estimated invites Google to show a \"price may change\" sheet for a\n // price that will not.\n priceStatus: GooglePayPriceStatus.TotalPriceStatusFinal,\n },\n (d: CardDetails) => {\n let t: Token\n try {\n t = token('google_pay', d)\n } catch (e) {\n reject(e)\n return\n }\n // Google Pay's sheet has already closed by the time this fires, so a\n // charge failure here is the host's to show on its own screen.\n if (detail?.charge) {\n detail.charge(t).then(() => resolve(t), reject)\n return\n }\n resolve(t)\n },\n (e: ErrorDetails) => reject(new Error(e.message)),\n () => reject(new Cancelled('google_pay')),\n )\n })\n }\n\n /** Nothing is held open between payments — every native rail is a sheet per call. */\n async release(): Promise<void> {}\n}\n\nfunction message(e: unknown): string {\n return e instanceof Error ? e.message : 'payment failed'\n}\n\n/**\n * A terminal for this device.\n *\n * Configuration is GLOBAL to the SDK — `setSquareApplicationId`,\n * `initializeApplePay` and `initializeGooglePay` set process-wide state rather\n * than returning handles — so this applies it and returns a thin object over it.\n * Two terminals with different application ids cannot coexist; the last one\n * built wins. That is the SDK's shape, not a choice made here.\n */\nexport async function terminal(config: Config): Promise<Terminal> {\n if (!config.applicationId || !config.locationId) {\n throw new Error('Square is not configured for this build')\n }\n SQIPCore.setSquareApplicationId(config.applicationId)\n SQIPCardEntry.setIOSCardEntryTheme(\n appearance(config.palette ?? PALETTE.dark, config.theme ?? 'dark'),\n )\n if (config.appleMerchantId) SQIPApplePay.initializeApplePay(config.appleMerchantId)\n SQIPGooglePay.initializeGooglePay(\n config.locationId,\n (config.environment ?? 'production').toLowerCase() === 'sandbox'\n ? GooglePayEnvironment.EnvironmentTest\n : GooglePayEnvironment.EnvironmentProduction,\n )\n return new Native(config)\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/native/theme.ts","../../src/native/terminal.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// A `Palette` rendered as the In-App Payments SDK's theme.\n//\n// Same table as the web backend paints the iframe with, so a card field looks\n// the same on a phone as it does in a browser without anyone maintaining two\n// sets of colours. The SDK takes {r,g,b,a} components rather than CSS, which is\n// the entire difference.\n\nimport type { Palette, Theme } from '../palette'\n\n/** The SDK's colour, 0-255 per channel with an optional 0-1 alpha. */\nexport interface Colour {\n r: number\n g: number\n b: number\n a?: number\n}\n\n/**\n * `SQIPCardEntry.setIOSCardEntryTheme`'s argument, as much of it as a palette can\n * answer for. Structurally assignable to the plugin's own `ThemeType`.\n */\nexport interface Appearance {\n backgroundColor?: Colour\n textColor?: Colour\n placeholderTextColor?: Colour\n tintColor?: Colour\n messageColor?: Colour\n errorColor?: Colour\n saveButtonTextColor?: Colour\n keyboardAppearance?: 'Light' | 'Dark'\n}\n\n/** `#rrggbb` -> components. Throws rather than guessing: a silent 0,0,0 is black. */\nexport function colour(hex: string): Colour {\n if (!/^#[0-9a-fA-F]{6}$/.test(hex)) throw new Error(`not an opaque hex colour: ${hex}`)\n return {\n r: parseInt(hex.slice(1, 3), 16),\n g: parseInt(hex.slice(3, 5), 16),\n b: parseInt(hex.slice(5, 7), 16),\n a: 1,\n }\n}\n\n/**\n * The native card sheet's appearance for `p`.\n *\n * TWO HONEST GAPS, both properties of the SDK rather than of this mapping:\n *\n * `border` has no counterpart. iOS draws its own field separators and exposes\n * no colour for them, so the palette's hairline is simply unused here. It is\n * not dropped by oversight and must not be mapped onto something else that\n * happens to accept a colour.\n *\n * THIS IS iOS ONLY. The setter is named for the platform it configures, and\n * Android's card entry is themed through native XML styles at build time —\n * there is no runtime channel to it at all. An Android build gets Square's\n * default sheet unless the app ships the corresponding style resource.\n *\n * `keyboardAppearance` is included because it is the one that gets noticed: a\n * light keyboard sliding up under a black sheet is the most visible seam on the\n * screen, and it is a single word to get right.\n */\nexport function appearance(p: Palette, theme: Theme): Appearance {\n return {\n backgroundColor: colour(p.field),\n textColor: colour(p.text),\n placeholderTextColor: colour(p.placeholder),\n // The caret and the active field's accent — the native rendering of the same\n // focus colour the web backend draws a ring in, and the same value for the\n // same reason.\n tintColor: colour(p.ring),\n messageColor: colour(p.placeholder),\n errorColor: colour(p.error),\n saveButtonTextColor: colour(p.text),\n keyboardAppearance: theme === 'dark' ? 'Dark' : 'Light',\n }\n}\n","// Square's In-App Payments SDK behind the same `Terminal` interface the web\n// backend satisfies.\n//\n// The two SDKs are not parallel and this file is where that stops being the\n// host's problem:\n//\n// The web SDK hands back objects you attach and tokenize. This one exposes\n// NAMESPACES OF CALLBACK FUNCTIONS that present native UI — so every rail here\n// is a callback trio (success / failure / cancel) wrapped into one promise.\n//\n// There is nothing to mount. Card entry is a sheet the OS presents, so\n// `mount` is absent rather than a no-op, which is the honest signal to a host\n// that there is no element to position.\n//\n// Card entry is a STATE MACHINE, not a form: the sheet stays open after\n// minting the token and is told, afterwards, whether the charge worked. See\n// `Detail.charge`.\n//\n// Cash App Pay and ACH are absent from `REACH.native` because this SDK has no\n// such rail — not because they are unfinished.\n\nimport {\n SQIPApplePay,\n SQIPCardEntry,\n SQIPCore,\n SQIPGooglePay,\n Brand,\n GooglePayEnvironment,\n GooglePayPriceStatus,\n ApplePayNonceSuccessState,\n type ApplePayNonceSuccessResult,\n type CardDetails,\n type ErrorDetails,\n type NonceSuccessResult,\n} from 'react-native-square-in-app-payments'\n\nimport {\n type Detail,\n type Method,\n type Tender,\n type Terminal,\n type Token,\n Cancelled,\n REACH,\n} from '../terminal'\nimport { type Palette, PALETTE, type Theme } from '../palette'\nimport { appearance } from './theme'\n\nexport interface Config {\n applicationId: string\n locationId: string\n /** `'sandbox'` tokenizes against Square's sandbox. Anything else is production. */\n environment?: string\n /**\n * The Apple merchant id (`merchant.com.example`) from the app's entitlements.\n * Apple Pay is simply not offered without one — there is nothing to fall back\n * to, and a button that cannot open a sheet is worse than no button.\n */\n appleMerchantId?: string\n /** The card sheet's colours. Defaults to @hanzo/design's dark table. */\n palette?: Palette\n /** Chooses the keyboard's own appearance under the sheet. Defaults to dark. */\n theme?: Theme\n}\n\n/** The plugin's numeric brand enum, as the string the web SDK already reports. */\nconst BRAND: Partial<Record<Brand, string>> = {\n [Brand.VISA]: 'VISA',\n [Brand.MASTERCARD]: 'MASTERCARD',\n [Brand.AMERICAN_EXPRESS]: 'AMERICAN_EXPRESS',\n [Brand.DISCOVER]: 'DISCOVER',\n [Brand.DISCOVER_DINERS]: 'DISCOVER_DINERS',\n [Brand.JCB]: 'JCB',\n [Brand.CHINA_UNION_PAY]: 'CHINA_UNION_PAY',\n [Brand.SQUARE_GIFT_CARD]: 'SQUARE_GIFT_CARD',\n}\n\nfunction token(method: Method, d: CardDetails): Token {\n if (!d.nonce) throw new Error(`${method} returned no token`)\n return {\n value: d.nonce,\n method,\n card: d.card\n ? {\n brand: d.card.brand === undefined ? undefined : BRAND[d.card.brand],\n last4: d.card.lastFourDigits,\n expMonth: d.card.expirationMonth,\n expYear: d.card.expirationYear,\n }\n : undefined,\n }\n}\n\nclass Native implements Terminal {\n constructor(private readonly config: Config) {}\n\n /**\n * What this DEVICE can pay with.\n *\n * Card and gift entry are always available — they are Square's own sheets and\n * need no wallet, no saved card and no capability beyond the SDK being linked.\n * The two wallets are asked, because both can be missing for reasons no\n * user-agent string would reveal: no card in Wallet, no Play Services, a\n * merchant id the app was not signed for.\n *\n * The tender is unused here. Native availability does not depend on the\n * amount, unlike the web SDK, where the total is bound into the wallet object\n * at construction — the interface carries it for the platform that needs it.\n */\n async offers(_tender: Tender): Promise<Method[]> {\n const out: Method[] = ['card', 'gift']\n if (this.config.appleMerchantId) {\n try {\n if (await SQIPApplePay.canUseApplePay()) out.push('apple_pay')\n } catch {\n // Not offerable on this device. Say nothing, show nothing.\n }\n }\n try {\n if (await SQIPGooglePay.canUseGooglePay()) out.push('google_pay')\n } catch {\n // Likewise.\n }\n return out.filter((m) => REACH.native.includes(m))\n }\n\n collect(method: Method, tender: Tender, detail?: Detail): Promise<Token> {\n if (!REACH.native.includes(method)) {\n return Promise.reject(new Error(`${method} is not a rail this SDK has`))\n }\n if (method === 'card' || method === 'gift') return this.sheet(method, detail)\n if (method === 'apple_pay') return this.apple(tender, detail)\n return this.google(tender, detail)\n }\n\n /**\n * Card and gift entry: one sheet, held open until the charge is known.\n *\n * The promise deliberately stays PENDING while the buyer retries. A declined\n * card returns `{ success: false }`, which keeps Square's sheet up with the\n * message shown and the card still entered — resolving or rejecting here would\n * settle a payment the buyer is still in the middle of making.\n */\n private sheet(method: 'card' | 'gift', detail?: Detail): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n const onNonce = async (d: CardDetails): Promise<NonceSuccessResult> => {\n let t: Token\n try {\n t = token(method, d)\n } catch (e) {\n return { success: false, errorMessage: message(e) }\n }\n if (detail?.charge) {\n try {\n await detail.charge(t)\n } catch (e) {\n // Stay on the sheet so the buyer can correct the card in place.\n return { success: false, errorMessage: message(e) }\n }\n }\n // `onCardEntryComplete` fires once the sheet has actually closed, so the\n // host's next screen never renders behind a dismissing modal.\n return { success: true, onCardEntryComplete: () => resolve(t) }\n }\n const onCancel = () => reject(new Cancelled(method))\n if (method === 'gift') SQIPCardEntry.startGiftCardEntryFlow(onNonce, onCancel)\n // `false` collects no postal code: the billing address belongs to the\n // payer and the host's own checkout already owns it, so asking again\n // inside the sheet is a second answer to one question.\n else SQIPCardEntry.startCardEntryFlow(false, onNonce, onCancel)\n })\n }\n\n private apple(tender: Tender, detail?: Detail): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n let settled: Token | null = null\n void SQIPApplePay.requestApplePayNonce(\n {\n price: tender.total.amount,\n summaryLabel: tender.label,\n countryCode: tender.country,\n currencyCode: tender.total.currency,\n },\n async (d: CardDetails): Promise<ApplePayNonceSuccessResult> => {\n let t: Token\n try {\n t = token('apple_pay', d)\n } catch (e) {\n return { state: ApplePayNonceSuccessState.Failure, errorMessage: message(e) }\n }\n if (detail?.charge) {\n try {\n await detail.charge(t)\n } catch (e) {\n // Apple's sheet shows its own failure state and then closes; there\n // is no retry-in-place here the way there is for card entry.\n return { state: ApplePayNonceSuccessState.Failure, errorMessage: message(e) }\n }\n }\n settled = t\n return { state: ApplePayNonceSuccessState.Succeeded }\n },\n (e: ErrorDetails) => reject(new Error(e.message)),\n // The completion callback is the ONLY place that knows how the sheet\n // ended, so it is the one that settles the promise.\n //\n // Annotated because `requestApplePayNonce` is overloaded and its\n // DEPRECATED signature takes a bare `() => void` here — TypeScript picks\n // that one and infers nothing, so the two arguments this needs would\n // silently be `any`.\n (state: ApplePayNonceSuccessState, errorMessage?: string) => {\n if (state === ApplePayNonceSuccessState.Succeeded && settled) resolve(settled)\n else if (state === ApplePayNonceSuccessState.Canceled) reject(new Cancelled('apple_pay'))\n else reject(new Error(errorMessage || 'Apple Pay could not be completed'))\n },\n )\n })\n }\n\n private google(tender: Tender, detail?: Detail): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n void SQIPGooglePay.requestGooglePayNonce(\n {\n price: tender.total.amount,\n currencyCode: tender.total.currency,\n // FINAL, because this total is what the host will charge. Quoting it\n // as estimated invites Google to show a \"price may change\" sheet for a\n // price that will not.\n priceStatus: GooglePayPriceStatus.TotalPriceStatusFinal,\n },\n (d: CardDetails) => {\n let t: Token\n try {\n t = token('google_pay', d)\n } catch (e) {\n reject(e)\n return\n }\n // Google Pay's sheet has already closed by the time this fires, so a\n // charge failure here is the host's to show on its own screen.\n if (detail?.charge) {\n detail.charge(t).then(() => resolve(t), reject)\n return\n }\n resolve(t)\n },\n (e: ErrorDetails) => reject(new Error(e.message)),\n () => reject(new Cancelled('google_pay')),\n )\n })\n }\n\n /** Nothing is held open between payments — every native rail is a sheet per call. */\n async release(): Promise<void> {}\n}\n\nfunction message(e: unknown): string {\n return e instanceof Error ? e.message : 'payment failed'\n}\n\n/**\n * A terminal for this device.\n *\n * Configuration is GLOBAL to the SDK — `setSquareApplicationId`,\n * `initializeApplePay` and `initializeGooglePay` set process-wide state rather\n * than returning handles — so this applies it and returns a thin object over it.\n * Two terminals with different application ids cannot coexist; the last one\n * built wins. That is the SDK's shape, not a choice made here.\n */\nexport async function terminal(config: Config): Promise<Terminal> {\n if (!config.applicationId || !config.locationId) {\n throw new Error('Square is not configured for this build')\n }\n SQIPCore.setSquareApplicationId(config.applicationId)\n SQIPCardEntry.setIOSCardEntryTheme(\n appearance(config.palette ?? PALETTE.dark, config.theme ?? 'dark'),\n )\n if (config.appleMerchantId) SQIPApplePay.initializeApplePay(config.appleMerchantId)\n SQIPGooglePay.initializeGooglePay(\n config.locationId,\n (config.environment ?? 'production').toLowerCase() === 'sandbox'\n ? GooglePayEnvironment.EnvironmentTest\n : GooglePayEnvironment.EnvironmentProduction,\n )\n return new Native(config)\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/web/sdk.ts","../../src/web/style.ts","../../src/web/terminal.ts"],"names":["style","Cancelled","REACH","PALETTE"],"mappings":"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","file":"index.cjs","sourcesContent":["// The Square Web Payments SDK, as much of it as we actually call, typed.\n//\n// Square ships no types for the global it installs, so this is the contract we\n// hold it to. Every member below is exercised by this package; nothing is\n// declared speculatively.\n\n/** What `tokenize()` resolves with. `status` is `'OK'` | `'CANCEL'` | an error. */\nexport interface Result {\n status: string\n token?: string\n details?: {\n card?: { brand?: string; last4?: string; expMonth?: number; expYear?: number }\n }\n errors?: Array<{ message: string }>\n}\n\nexport interface Tokenizer {\n tokenize(options?: Record<string, unknown>): Promise<Result>\n /**\n * Cash App Pay and ACH deliver their token on an `ontokenization` EVENT rather\n * than from tokenize()'s return — the buyer leaves the page (for Cash App, or\n * for their bank's login) and comes back. A caller that only reads the return\n * value gets nothing from either, silently, on a payment that succeeded.\n */\n addEventListener?(event: string, handler: (e: unknown) => void): void\n /**\n * Optional because a WALLET may draw itself (Apple Pay does) rather than\n * attach. The second argument is Cash App Pay's button options\n * (`{ shape, width }`); every other rail ignores it.\n */\n attach?(selector: string, options?: Record<string, unknown>): Promise<void>\n destroy?(): Promise<void>\n}\n\n/**\n * The CARD element always attaches and always destroys — it owns an iframe. Kept\n * distinct from the wallet tokenizer so the card path is not forced to\n * optional-chain calls that can never be absent.\n */\nexport interface Element extends Tokenizer {\n attach(selector: string): Promise<void>\n destroy(): Promise<void>\n}\n\nexport interface Request {\n countryCode: string\n currencyCode: string\n total: { amount: string; label: string }\n}\n\nexport interface Payments {\n card(options?: Record<string, unknown>): Promise<Element>\n /**\n * A Square gift card. Its own constructor and its own container — a gift card\n * is a balance instrument, not a credit line, so Square keeps the two elements\n * separate rather than switching one on a brand.\n *\n * It attaches and tokenizes exactly like a card, with one difference that\n * matters to a caller: its `tokenize()` takes NO verification details. SCA\n * covers cards, and a gift card has no issuer to challenge.\n */\n giftCard(options?: Record<string, unknown>): Promise<Element>\n paymentRequest(req: Request): unknown\n applePay(req: unknown): Promise<Tokenizer>\n googlePay(req: unknown): Promise<Tokenizer>\n cashAppPay(req: unknown, opts: Record<string, unknown>): Promise<Tokenizer>\n /**\n * ACH bank transfer. Square runs PLAID'S instant bank authentication itself,\n * so this needs no Plaid account of ours — which is why the rail sat marked\n * \"coming soon\" for want of an integration that was already included. US-only.\n *\n * THE ARGUMENT IS OPTIONAL BECAUSE SQUARE ACCEPTS TWO SHAPES, and a type that\n * admitted only one would reject working code. The redirect form\n * (`ach({ redirectURI, transactionId })`, what this package calls and what\n * hanzoai/pay has run in production) sends the buyer to their bank and back;\n * Square's own quickstart instead calls `ach()` bare and passes\n * `{ accountHolderName, intent, amount, currency }` to `tokenize()`. Both\n * deliver the token the same way — on the event, never from the return.\n */\n ach(opts?: { redirectURI?: string; transactionId?: string }): Promise<Tokenizer>\n}\n\ninterface SDK {\n payments(appId: string, locationId: string): Promise<Payments>\n}\n\ndeclare global {\n interface Window {\n Square?: SDK\n }\n}\n\nexport const PRODUCTION = 'https://web.squarecdn.com/v1/square.js'\nexport const SANDBOX = 'https://sandbox.web.squarecdn.com/v1/square.js'\n\nlet pending: Promise<void> | null = null\n\n/** Load the SDK once per page, whichever surface asks first. */\nexport function load(src: string): Promise<void> {\n if (pending) return pending\n if (typeof window !== 'undefined' && window.Square) return (pending = Promise.resolve())\n pending = new Promise((resolve, reject) => {\n const existing = document.querySelector(`script[src=\"${src}\"]`)\n if (existing) {\n existing.addEventListener('load', () => resolve())\n existing.addEventListener('error', () => reject(new Error('Square SDK failed to load')))\n return\n }\n const s = document.createElement('script')\n s.src = src\n s.async = true\n s.onload = () => resolve()\n s.onerror = () => reject(new Error('Square SDK failed to load'))\n document.head.appendChild(s)\n })\n return pending\n}\n\n/** Drop the memoized load. For tests only — a page loads the SDK once. */\nexport function forget(): void {\n pending = null\n}\n","// A `Palette` rendered as the style object `payments.card({ style })` accepts.\n//\n// Measured against the live SDK, not read off a doc page. Where a comment says\n// \"confirmed\", it was driven and inspected.\n\nimport type { Palette } from '../palette'\n\n/** A selector's CSS property bag. */\nexport type Rules = Record<string, string>\n\n/**\n * What `payments.card({ style })` accepts: selector -> properties, plus `@media`\n * keys whose value is a nested block of the same. Square resolves the media query\n * INSIDE the iframe, which is the only way in — the frame cannot see the page's\n * own breakpoints.\n */\nexport type Style = Record<string, Rules | Record<string, Rules>>\n\n/**\n * One selector's properties.\n *\n * A `@media` key holds a nested block, so the map's value type is a union and\n * every read of a plain selector would otherwise need its own cast. This is that\n * cast, written once, where the shape is known.\n */\nexport function rules(style: Style, selector: string): Rules {\n return style[selector] as Rules\n}\n\n/** The input/button radius of @hanzo/design's ramp (--radius-sm), in the px Square expects. */\nconst RADIUS = '6px'\n\n/**\n * The body size of @hanzo/design's ramp (--text-sm), which is what a checkout's\n * own inputs render at. It was 14px — one rung above every field beside it, so\n * the card strip's placeholders were visibly larger than the address labels.\n */\nconst SIZE = '13px'\n\n/**\n * The ONE focus indicator, drawn around the container in `pin()`.\n *\n * These two values are the host's, not a choice made here: they are what\n * `:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px }`\n * renders, which is the rule a Hanzo surface already applies to every button,\n * link and input on the page. The card strip is the one control that cannot\n * inherit it — the fields are in a frame that has no idea a page exists — so it\n * is restated here, once, in the same numbers.\n */\nconst RING = '2px'\nconst RING_GAP = '2px'\n\n/**\n * The style object for a card element painted in `p`.\n *\n * THE GROUND AND THE INK ARE ONE PALETTE, and that is the whole point. The field\n * shipped as `input.backgroundColor: 'transparent'` beside `input.color:\n * '#fafafa'` — so Square painted near-white digits onto its own default white\n * iframe and the customer's card number was invisible as they typed it. Two\n * properties, set in two places, describing one surface. Reading them off one\n * `Palette` is what stops them disagreeing again; `legible()` proves they have not.\n *\n * `input.backgroundColor` IS the supported way in, and it is not a workaround.\n * Square maps that one property onto the container behind the iframe rather than\n * onto the input:\n *\n * selectorPropertyMappings[input] = [{ property: 'backgroundColor',\n * toSelectors: ['#<id>.sq-card-wrapper .sq-card-iframe-container'] }]\n *\n * Measured, not read: driving build 1.84.0 with the object below and inspecting\n * the result gives `.sq-card-iframe-container { background-color: #0a0a0a }` in\n * the parent document, with `<body>`, `<html>` and all four inputs inside the\n * cross-origin frame computing to `rgba(0,0,0,0)`. The frame is transparent by\n * design and the container is the surface — so painting the container IS painting\n * the field.\n *\n * AND IT IS STILL NOT ENOUGH ON ITS OWN — see `pin()` below, which this package\n * applies for you. The declaration above lands and is then overpainted, so a\n * style object shipped without that rule is accepted and silently ignored. That\n * gap was misread once as an SDK bug to wait out; it was ours.\n *\n * `.input-container { backgroundColor }` is ALSO accepted on 1.84.0 (it throws\n * nothing). It is still not set: it would be a second way to paint the one\n * surface `input.backgroundColor` already paints, and the two could then\n * disagree. One property, one surface.\n *\n * `fontFamily` is deliberately absent: Square validates it against its own\n * loadable list and throws on both CSS-wide stacks and arbitrary names, which\n * blocks the iframe from attaching at all. Its default is a system sans, which is\n * what --font-sans resolves to anyway.\n */\nexport function style(p: Palette): Style {\n return {\n // Borders and radius ONLY. Square's allowlist for this selector is\n // borderColor/borderRadius/borderWidth; anything else throws \"Invalid style\n // property\", and Square rejects the WHOLE style object when it does — so one\n // stray property leaves the card unstyled and white.\n '.input-container': {\n borderColor: p.border,\n borderRadius: RADIUS,\n borderWidth: '1px',\n },\n // THE HAIRLINE DOES NOT MOVE ON FOCUS, and that is the point. Square spends\n // this value twice — on the container's border AND on a `::before` ring at\n // `inset: -1px` — so any colour but the resting one puts a second ring\n // immediately inside the one `pin()` draws. Two rings for one event is the\n // exact defect this pass removes from the address fields; the card strip\n // must not reintroduce it one row lower.\n '.input-container.is-focus': { borderColor: p.border },\n '.input-container.is-error': { borderColor: p.error },\n input: {\n backgroundColor: p.field,\n color: p.text,\n fontSize: SIZE,\n },\n // 16px on a small screen, because iOS Safari ZOOMS the page when a field\n // under 16px takes focus — on a card form that throws the layout sideways\n // mid-number and there is no way to zoom back without losing the caret.\n // Square's own dark-mode example carries this media query for the same\n // reason; it is the one place the field may disagree with the page's ramp.\n '@media screen and (max-width: 600px)': {\n input: { fontSize: '16px' },\n },\n 'input::placeholder': { color: p.placeholder },\n 'input.is-error': { color: p.error },\n '.message-text': { color: p.placeholder },\n '.message-text.is-error': { color: p.error },\n '.message-icon': { color: p.placeholder },\n '.message-icon.is-error': { color: p.error },\n }\n}\n\n/**\n * The two rules Square's own style object cannot express.\n *\n * Square's `input.backgroundColor` is accepted and then IGNORED: the field\n * renders white on a black checkout no matter what is passed. The cause is not\n * Square's, and it is one property. A page that sets `color-scheme` on\n * `<html>` — which is the correct thing to do, and what every themed app does so\n * the UA paints scrollbars and form controls to match — leaks it INTO the\n * cross-origin iframe, because `color-scheme` inherits. Inside the frame the UA\n * then paints form controls on its own scheme background, over anything the SDK\n * declared. Pinning the container back to `auto` lets our ground through.\n *\n * (A Square forum user reported the same thing after two years of the docs' own\n * recipe not working. Same fix, arrived at from the other end.)\n *\n * THIS IS APPLIED FOR YOU, by `terminal()`, before any card can attach — it must\n * be in the stylesheet BEFORE the element mounts, because the container is styled\n * as the card attaches, and a rule added afterwards is a rule that arrived too\n * late. It is not left to a host to remember, and it is not a stylesheet a host\n * can forget to import: this package produced the style object, so this package\n * owes the one rule that makes it mean anything.\n *\n * THE SECOND RULE IS THE FOCUS RING, and it is here for the mirror-image reason:\n * an outline has to be drawn OUTSIDE the field, and everything Square will style\n * for us lives inside a frame that ends at the field's edge. The seam is\n * `sq-focus`, which Square adds to `.sq-card-wrapper` in THIS document while a\n * card input holds focus — the class its own `focusClassAdded`/`focusClassRemoved`\n * events are named for. So the ring is plain CSS: no listener, no state, nothing\n * to unsubscribe, and it cannot drift out of step with the field it surrounds.\n *\n * Measured on 1.84.0: Square generates\n * `#<wrapper>.sq-focus .sq-card-iframe-container::before { inset: -1px }` as its\n * own focus ring, which is why `style()` paints the focused hairline the resting\n * colour — that pseudo-ring then has nothing to draw and this is the only ring\n * the buyer sees.\n *\n * A no-op without a document. Called again with a different palette (a theme\n * change) it REWRITES its rules rather than bailing: there is one element, always\n * carrying the current colours. Bailing early would have left the ring painted in\n * the palette of a theme the page had already left.\n */\nexport function pin(p: Palette, doc?: Document): void {\n const d = doc ?? (typeof document === 'undefined' ? undefined : document)\n if (!d) return\n const MARK = 'hanzo-pay-scheme'\n let el = d.getElementById(MARK) as HTMLStyleElement | null\n if (!el) {\n el = d.createElement('style')\n el.id = MARK\n d.head.appendChild(el)\n }\n el.textContent =\n '.sq-card-iframe-container{color-scheme:auto}' +\n `.sq-card-wrapper.sq-focus .sq-card-iframe-container{outline:${RING} solid ${p.ring};outline-offset:${RING_GAP}}`\n}\n","// The Square Web Payments SDK behind the one `Terminal` interface.\n//\n// Every rail here mints the SAME single-use token, bound for the same server\n// call — so none of them needed server work beyond the one that already existed.\n// What they do NOT share is how the token arrives, and treating them as one shape\n// is a silent failure in three of the six:\n//\n// card, gift attach() into a container, then tokenize() returns the token.\n// google_pay attach() into a container Square draws its button in, then\n// tokenize() from our click.\n// apple_pay NO attach() — Square's docs are explicit. The host renders the\n// button (Apple's own CSS appearance), and tokenize() must be\n// called IMMEDIATELY in the click handler.\n// cash_app attach(), and then the token arrives on an `ontokenization`\n// EVENT after the buyer approves in the app or by QR. Calling\n// tokenize() ourselves does nothing and the payment never lands.\n// ach NO attach() — the flow IS tokenize(). The token likewise arrives\n// on `ontokenization`, because the buyer leaves for their bank.\n\nimport {\n type Detail,\n type Method,\n type Tender,\n type Terminal,\n type Token,\n Cancelled,\n REACH,\n} from '../terminal'\nimport { type Palette, PALETTE } from '../palette'\nimport { load, PRODUCTION, SANDBOX, type Payments, type Result, type Tokenizer } from './sdk'\nimport { pin, style } from './style'\n\n/** The org's PUBLIC Square config, exactly as commerce publishes it. */\nexport interface Config {\n applicationId: string\n locationId: string\n /** `'sandbox'` tokenizes against Square's sandbox. Anything else is production. */\n environment?: string\n /** The card field's colours. Defaults to @hanzo/design's dark table. */\n palette?: Palette\n}\n\n/** Rails that draw themselves into a container the host positions. */\nconst DRAWN: readonly Method[] = ['card', 'gift', 'google_pay', 'cash_app']\n\nfunction token(method: Method, r: Result): Token {\n // CANCEL is the buyer changing their mind. It is not a failure and must never\n // be shown as one.\n if (String(r.status).toUpperCase() === 'CANCEL') throw new Cancelled(method)\n if (r.status !== 'OK' || !r.token) {\n throw new Error(r.errors?.[0]?.message ?? `${method} could not be completed`)\n }\n return { value: r.token, method, card: r.details?.card }\n}\n\n/**\n * The token that arrives on an EVENT rather than from a return — Cash App Pay\n * and ACH, for the same underlying reason: the buyer leaves the page.\n *\n * One listener per collect(), removed when it settles. Square's own quickstart\n * registers a fresh listener on every submission and never removes one, so a\n * buyer who retries gets their token delivered to every previous attempt too.\n */\nfunction awaited(method: Method, t: Tokenizer): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n t.addEventListener?.('ontokenization', (e: unknown) => {\n const detail = (e as { detail?: { tokenResult?: Result; error?: unknown } })?.detail\n if (detail?.error) {\n reject(new Error(`${method} could not be completed`))\n return\n }\n if (!detail?.tokenResult) return\n try {\n resolve(token(method, detail.tokenResult))\n } catch (err) {\n reject(err)\n }\n })\n })\n}\n\nclass Web implements Terminal {\n private readonly built = new Map<Method, Tokenizer>()\n private readonly palette: Palette\n\n constructor(\n private readonly payments: Payments,\n palette: Palette,\n ) {\n this.palette = palette\n }\n\n /**\n * Which rails can REALLY pay here, asked of the SDK one at a time.\n *\n * Building the rail IS the probe, and it is the only answer that accounts for\n * the browser, the device, the buyer's saved cards AND whether the merchant\n * account has the rail switched on. A rejection is a plain \"not here\", not an\n * error worth showing anyone.\n *\n * The built objects are KEPT, and that is load-bearing rather than a cache:\n * `collect('apple_pay')` must reach `tokenize()` with nothing awaited in front\n * of it, so the object it needs has to already exist by then.\n */\n async offers(tender: Tender): Promise<Method[]> {\n const out: Method[] = []\n for (const method of REACH.web) {\n try {\n this.built.set(method, await this.build(method, tender))\n out.push(method)\n } catch {\n // Not offerable here. Say nothing and show nothing.\n }\n }\n return out\n }\n\n private async build(method: Method, tender: Tender): Promise<Tokenizer> {\n if (method === 'card') return this.payments.card({ style: style(this.palette) })\n if (method === 'gift') return this.payments.giftCard()\n if (method === 'ach') {\n return this.payments.ach({\n // Square REJECTS a redirectURI carrying a query string, so the page's own\n // path is used and anything that must survive the trip travels separately.\n redirectURI:\n typeof window === 'undefined'\n ? ''\n : window.location.origin + window.location.pathname,\n transactionId: `${Date.now()}`,\n })\n }\n // A FRESH paymentRequest per wallet: Square binds it into the object it\n // builds, so two wallets sharing one request is one wallet quoting the\n // other's total.\n const req = this.payments.paymentRequest({\n countryCode: tender.country,\n currencyCode: tender.total.currency,\n total: { amount: tender.total.amount, label: tender.label },\n })\n if (method === 'apple_pay') return this.payments.applePay(req)\n if (method === 'google_pay') return this.payments.googlePay(req)\n return this.payments.cashAppPay(req, {\n // Where Cash App returns a MOBILE buyer. Desktop uses the QR and never\n // leaves the page.\n redirectURL: typeof window === 'undefined' ? '' : window.location.href,\n referenceId: `pay-${tender.total.amount}`,\n })\n }\n\n /** Draw a rail that renders inline. A rail that draws nothing is a no-op, not an error. */\n async mount(method: Method, target: string): Promise<void> {\n const t = this.built.get(method)\n if (!t) throw new Error(`${method} was not offered here`)\n if (!DRAWN.includes(method)) return\n await t.attach?.(target)\n }\n\n /**\n * Tokenize.\n *\n * NOTHING IS AWAITED BEFORE `tokenize()`. Apple refuses a payment sheet that\n * was not opened directly by the gesture that asked for it, so a single `await`\n * placed above the call — loading the SDK, looking a rail up asynchronously,\n * re-reading a total — silently breaks Apple Pay and nothing else. That is why\n * `offers()` builds every rail up front and this only reads a map.\n */\n collect(method: Method, tender: Tender, detail?: Detail): Promise<Token> {\n const t = this.built.get(method)\n if (!t) return Promise.reject(new Error(`${method} was not offered here`))\n\n // Cash App draws its own button and delivers on the event; a tokenize() call\n // here does nothing at all.\n if (method === 'cash_app') return awaited(method, t)\n\n if (method === 'ach') {\n if (!detail?.name) {\n return Promise.reject(new Error('ACH needs the account holder’s name'))\n }\n const arrived = awaited(method, t)\n // The flow IS tokenize() for ACH — it opens the bank login — but the token\n // comes back on the event, so the return value is deliberately dropped.\n void t.tokenize({\n accountHolderName: detail.name,\n intent: 'CHARGE',\n amount: tender.total.amount,\n currency: tender.total.currency,\n })\n return arrived\n }\n\n return t.tokenize(detail?.verify).then((r) => token(method, r))\n }\n\n async release(): Promise<void> {\n // Square's `destroy()` empties the mount node when it resolves, so a create\n // that overlaps a destroy gets its fresh iframe swept away by the old\n // element's cleanup — the form then sits empty forever with no error to\n // explain it. Callers await this before building the next terminal, which is\n // what makes a theme change survivable.\n await Promise.all([...this.built.values()].map((t) => t.destroy?.().catch(() => undefined)))\n this.built.clear()\n }\n}\n\n/**\n * A terminal for this browser. Loads the SDK once per page, whichever surface\n * asks first.\n *\n * The palette is fixed at construction because SQUARE HAS NO API TO RESTYLE A\n * LIVE CARD — the fields are a cross-origin iframe, told their colours once, at\n * creation. Following a theme change therefore means `release()` then a new\n * terminal, and making the palette a constructor argument is what forces that to\n * happen by construction rather than by remembering to.\n */\nexport async function terminal(config: Config): Promise<Terminal> {\n if (!config.applicationId || !config.locationId) {\n throw new Error('Square is not configured for this deployment')\n }\n const palette = config.palette ?? PALETTE.dark\n // BEFORE the SDK loads, let alone attaches. The container is styled as the\n // card mounts, so these rules have to already be in the sheet by then — see\n // `pin()`. Doing it here is what makes it impossible to ship the style object\n // without the rules that make it visible.\n pin(palette)\n const src = (config.environment ?? 'production').toLowerCase() === 'sandbox' ? SANDBOX : PRODUCTION\n await load(src)\n if (typeof window === 'undefined' || !window.Square) {\n throw new Error('Square SDK failed to load')\n }\n const payments = await window.Square.payments(config.applicationId, config.locationId)\n return new Web(payments, palette)\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/web/sdk.ts","../../src/web/style.ts","../../src/web/terminal.ts"],"names":["style"],"mappings":"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","file":"index.js","sourcesContent":["// The Square Web Payments SDK, as much of it as we actually call, typed.\n//\n// Square ships no types for the global it installs, so this is the contract we\n// hold it to. Every member below is exercised by this package; nothing is\n// declared speculatively.\n\n/** What `tokenize()` resolves with. `status` is `'OK'` | `'CANCEL'` | an error. */\nexport interface Result {\n status: string\n token?: string\n details?: {\n card?: { brand?: string; last4?: string; expMonth?: number; expYear?: number }\n }\n errors?: Array<{ message: string }>\n}\n\nexport interface Tokenizer {\n tokenize(options?: Record<string, unknown>): Promise<Result>\n /**\n * Cash App Pay and ACH deliver their token on an `ontokenization` EVENT rather\n * than from tokenize()'s return — the buyer leaves the page (for Cash App, or\n * for their bank's login) and comes back. A caller that only reads the return\n * value gets nothing from either, silently, on a payment that succeeded.\n */\n addEventListener?(event: string, handler: (e: unknown) => void): void\n /**\n * Optional because a WALLET may draw itself (Apple Pay does) rather than\n * attach. The second argument is Cash App Pay's button options\n * (`{ shape, width }`); every other rail ignores it.\n */\n attach?(selector: string, options?: Record<string, unknown>): Promise<void>\n destroy?(): Promise<void>\n}\n\n/**\n * The CARD element always attaches and always destroys — it owns an iframe. Kept\n * distinct from the wallet tokenizer so the card path is not forced to\n * optional-chain calls that can never be absent.\n */\nexport interface Element extends Tokenizer {\n attach(selector: string): Promise<void>\n destroy(): Promise<void>\n}\n\nexport interface Request {\n countryCode: string\n currencyCode: string\n total: { amount: string; label: string }\n}\n\nexport interface Payments {\n card(options?: Record<string, unknown>): Promise<Element>\n /**\n * A Square gift card. Its own constructor and its own container — a gift card\n * is a balance instrument, not a credit line, so Square keeps the two elements\n * separate rather than switching one on a brand.\n *\n * It attaches and tokenizes exactly like a card, with one difference that\n * matters to a caller: its `tokenize()` takes NO verification details. SCA\n * covers cards, and a gift card has no issuer to challenge.\n */\n giftCard(options?: Record<string, unknown>): Promise<Element>\n paymentRequest(req: Request): unknown\n applePay(req: unknown): Promise<Tokenizer>\n googlePay(req: unknown): Promise<Tokenizer>\n cashAppPay(req: unknown, opts: Record<string, unknown>): Promise<Tokenizer>\n /**\n * ACH bank transfer. Square runs PLAID'S instant bank authentication itself,\n * so this needs no Plaid account of ours — which is why the rail sat marked\n * \"coming soon\" for want of an integration that was already included. US-only.\n *\n * THE ARGUMENT IS OPTIONAL BECAUSE SQUARE ACCEPTS TWO SHAPES, and a type that\n * admitted only one would reject working code. The redirect form\n * (`ach({ redirectURI, transactionId })`, what this package calls and what\n * hanzoai/pay has run in production) sends the buyer to their bank and back;\n * Square's own quickstart instead calls `ach()` bare and passes\n * `{ accountHolderName, intent, amount, currency }` to `tokenize()`. Both\n * deliver the token the same way — on the event, never from the return.\n */\n ach(opts?: { redirectURI?: string; transactionId?: string }): Promise<Tokenizer>\n}\n\ninterface SDK {\n payments(appId: string, locationId: string): Promise<Payments>\n}\n\ndeclare global {\n interface Window {\n Square?: SDK\n }\n}\n\nexport const PRODUCTION = 'https://web.squarecdn.com/v1/square.js'\nexport const SANDBOX = 'https://sandbox.web.squarecdn.com/v1/square.js'\n\nlet pending: Promise<void> | null = null\n\n/** Load the SDK once per page, whichever surface asks first. */\nexport function load(src: string): Promise<void> {\n if (pending) return pending\n if (typeof window !== 'undefined' && window.Square) return (pending = Promise.resolve())\n pending = new Promise((resolve, reject) => {\n const existing = document.querySelector(`script[src=\"${src}\"]`)\n if (existing) {\n existing.addEventListener('load', () => resolve())\n existing.addEventListener('error', () => reject(new Error('Square SDK failed to load')))\n return\n }\n const s = document.createElement('script')\n s.src = src\n s.async = true\n s.onload = () => resolve()\n s.onerror = () => reject(new Error('Square SDK failed to load'))\n document.head.appendChild(s)\n })\n return pending\n}\n\n/** Drop the memoized load. For tests only — a page loads the SDK once. */\nexport function forget(): void {\n pending = null\n}\n","// A `Palette` rendered as the style object `payments.card({ style })` accepts.\n//\n// Measured against the live SDK, not read off a doc page. Where a comment says\n// \"confirmed\", it was driven and inspected.\n\nimport type { Palette } from '../palette'\n\n/** A selector's CSS property bag. */\nexport type Rules = Record<string, string>\n\n/**\n * What `payments.card({ style })` accepts: selector -> properties, plus `@media`\n * keys whose value is a nested block of the same. Square resolves the media query\n * INSIDE the iframe, which is the only way in — the frame cannot see the page's\n * own breakpoints.\n */\nexport type Style = Record<string, Rules | Record<string, Rules>>\n\n/**\n * One selector's properties.\n *\n * A `@media` key holds a nested block, so the map's value type is a union and\n * every read of a plain selector would otherwise need its own cast. This is that\n * cast, written once, where the shape is known.\n */\nexport function rules(style: Style, selector: string): Rules {\n return style[selector] as Rules\n}\n\n/** The input/button radius of @hanzo/design's ramp (--radius-sm), in the px Square expects. */\nconst RADIUS = '6px'\n\n/**\n * The body size of @hanzo/design's ramp (--text-sm), which is what a checkout's\n * own inputs render at. It was 14px — one rung above every field beside it, so\n * the card strip's placeholders were visibly larger than the address labels.\n */\nconst SIZE = '13px'\n\n/**\n * The ONE focus indicator, drawn around the container in `pin()`.\n *\n * These two values are the host's, not a choice made here: they are what\n * `:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px }`\n * renders, which is the rule a Hanzo surface already applies to every button,\n * link and input on the page. The card strip is the one control that cannot\n * inherit it — the fields are in a frame that has no idea a page exists — so it\n * is restated here, once, in the same numbers.\n */\nconst RING = '2px'\nconst RING_GAP = '2px'\n\n/**\n * The style object for a card element painted in `p`.\n *\n * THE GROUND AND THE INK ARE ONE PALETTE, and that is the whole point. The field\n * shipped as `input.backgroundColor: 'transparent'` beside `input.color:\n * '#fafafa'` — so Square painted near-white digits onto its own default white\n * iframe and the customer's card number was invisible as they typed it. Two\n * properties, set in two places, describing one surface. Reading them off one\n * `Palette` is what stops them disagreeing again; `legible()` proves they have not.\n *\n * `input.backgroundColor` IS the supported way in, and it is not a workaround.\n * Square maps that one property onto the container behind the iframe rather than\n * onto the input:\n *\n * selectorPropertyMappings[input] = [{ property: 'backgroundColor',\n * toSelectors: ['#<id>.sq-card-wrapper .sq-card-iframe-container'] }]\n *\n * Measured, not read: driving build 1.84.0 with the object below and inspecting\n * the result gives `.sq-card-iframe-container { background-color: #0a0a0a }` in\n * the parent document, with `<body>`, `<html>` and all four inputs inside the\n * cross-origin frame computing to `rgba(0,0,0,0)`. The frame is transparent by\n * design and the container is the surface — so painting the container IS painting\n * the field.\n *\n * AND IT IS STILL NOT ENOUGH ON ITS OWN — see `pin()` below, which this package\n * applies for you. The declaration above lands and is then overpainted, so a\n * style object shipped without that rule is accepted and silently ignored. That\n * gap was misread once as an SDK bug to wait out; it was ours.\n *\n * `.input-container { backgroundColor }` is ALSO accepted on 1.84.0 (it throws\n * nothing). It is still not set: it would be a second way to paint the one\n * surface `input.backgroundColor` already paints, and the two could then\n * disagree. One property, one surface.\n *\n * `fontFamily` is deliberately absent: Square validates it against its own\n * loadable list and throws on both CSS-wide stacks and arbitrary names, which\n * blocks the iframe from attaching at all. Its default is a system sans, which is\n * what --font-sans resolves to anyway.\n */\nexport function style(p: Palette): Style {\n return {\n // Borders and radius ONLY. Square's allowlist for this selector is\n // borderColor/borderRadius/borderWidth; anything else throws \"Invalid style\n // property\", and Square rejects the WHOLE style object when it does — so one\n // stray property leaves the card unstyled and white.\n '.input-container': {\n borderColor: p.border,\n borderRadius: RADIUS,\n borderWidth: '1px',\n },\n // THE HAIRLINE DOES NOT MOVE ON FOCUS, and that is the point. Square spends\n // this value twice — on the container's border AND on a `::before` ring at\n // `inset: -1px` — so any colour but the resting one puts a second ring\n // immediately inside the one `pin()` draws. Two rings for one event is the\n // exact defect this pass removes from the address fields; the card strip\n // must not reintroduce it one row lower.\n '.input-container.is-focus': { borderColor: p.border },\n '.input-container.is-error': { borderColor: p.error },\n input: {\n backgroundColor: p.field,\n color: p.text,\n fontSize: SIZE,\n },\n // 16px on a small screen, because iOS Safari ZOOMS the page when a field\n // under 16px takes focus — on a card form that throws the layout sideways\n // mid-number and there is no way to zoom back without losing the caret.\n // Square's own dark-mode example carries this media query for the same\n // reason; it is the one place the field may disagree with the page's ramp.\n '@media screen and (max-width: 600px)': {\n input: { fontSize: '16px' },\n },\n 'input::placeholder': { color: p.placeholder },\n 'input.is-error': { color: p.error },\n '.message-text': { color: p.placeholder },\n '.message-text.is-error': { color: p.error },\n '.message-icon': { color: p.placeholder },\n '.message-icon.is-error': { color: p.error },\n }\n}\n\n/**\n * The two rules Square's own style object cannot express.\n *\n * Square's `input.backgroundColor` is accepted and then IGNORED: the field\n * renders white on a black checkout no matter what is passed. The cause is not\n * Square's, and it is one property. A page that sets `color-scheme` on\n * `<html>` — which is the correct thing to do, and what every themed app does so\n * the UA paints scrollbars and form controls to match — leaks it INTO the\n * cross-origin iframe, because `color-scheme` inherits. Inside the frame the UA\n * then paints form controls on its own scheme background, over anything the SDK\n * declared. Pinning the container back to `auto` lets our ground through.\n *\n * (A Square forum user reported the same thing after two years of the docs' own\n * recipe not working. Same fix, arrived at from the other end.)\n *\n * THIS IS APPLIED FOR YOU, by `terminal()`, before any card can attach — it must\n * be in the stylesheet BEFORE the element mounts, because the container is styled\n * as the card attaches, and a rule added afterwards is a rule that arrived too\n * late. It is not left to a host to remember, and it is not a stylesheet a host\n * can forget to import: this package produced the style object, so this package\n * owes the one rule that makes it mean anything.\n *\n * THE SECOND RULE IS THE FOCUS RING, and it is here for the mirror-image reason:\n * an outline has to be drawn OUTSIDE the field, and everything Square will style\n * for us lives inside a frame that ends at the field's edge. The seam is\n * `sq-focus`, which Square adds to `.sq-card-wrapper` in THIS document while a\n * card input holds focus — the class its own `focusClassAdded`/`focusClassRemoved`\n * events are named for. So the ring is plain CSS: no listener, no state, nothing\n * to unsubscribe, and it cannot drift out of step with the field it surrounds.\n *\n * Measured on 1.84.0: Square generates\n * `#<wrapper>.sq-focus .sq-card-iframe-container::before { inset: -1px }` as its\n * own focus ring, which is why `style()` paints the focused hairline the resting\n * colour — that pseudo-ring then has nothing to draw and this is the only ring\n * the buyer sees.\n *\n * A no-op without a document. Called again with a different palette (a theme\n * change) it REWRITES its rules rather than bailing: there is one element, always\n * carrying the current colours. Bailing early would have left the ring painted in\n * the palette of a theme the page had already left.\n */\nexport function pin(p: Palette, doc?: Document): void {\n const d = doc ?? (typeof document === 'undefined' ? undefined : document)\n if (!d) return\n const MARK = 'hanzo-pay-scheme'\n let el = d.getElementById(MARK) as HTMLStyleElement | null\n if (!el) {\n el = d.createElement('style')\n el.id = MARK\n d.head.appendChild(el)\n }\n el.textContent =\n '.sq-card-iframe-container{color-scheme:auto}' +\n `.sq-card-wrapper.sq-focus .sq-card-iframe-container{outline:${RING} solid ${p.ring};outline-offset:${RING_GAP}}`\n}\n","// The Square Web Payments SDK behind the one `Terminal` interface.\n//\n// Every rail here mints the SAME single-use token, bound for the same server\n// call — so none of them needed server work beyond the one that already existed.\n// What they do NOT share is how the token arrives, and treating them as one shape\n// is a silent failure in three of the six:\n//\n// card, gift attach() into a container, then tokenize() returns the token.\n// google_pay attach() into a container Square draws its button in, then\n// tokenize() from our click.\n// apple_pay NO attach() — Square's docs are explicit. The host renders the\n// button (Apple's own CSS appearance), and tokenize() must be\n// called IMMEDIATELY in the click handler.\n// cash_app attach(), and then the token arrives on an `ontokenization`\n// EVENT after the buyer approves in the app or by QR. Calling\n// tokenize() ourselves does nothing and the payment never lands.\n// ach NO attach() — the flow IS tokenize(). The token likewise arrives\n// on `ontokenization`, because the buyer leaves for their bank.\n\nimport {\n type Detail,\n type Method,\n type Tender,\n type Terminal,\n type Token,\n Cancelled,\n REACH,\n} from '../terminal'\nimport { type Palette, PALETTE } from '../palette'\nimport { load, PRODUCTION, SANDBOX, type Payments, type Result, type Tokenizer } from './sdk'\nimport { pin, style } from './style'\n\n/** The org's PUBLIC Square config, exactly as commerce publishes it. */\nexport interface Config {\n applicationId: string\n locationId: string\n /** `'sandbox'` tokenizes against Square's sandbox. Anything else is production. */\n environment?: string\n /** The card field's colours. Defaults to @hanzo/design's dark table. */\n palette?: Palette\n}\n\n/** Rails that draw themselves into a container the host positions. */\nconst DRAWN: readonly Method[] = ['card', 'gift', 'google_pay', 'cash_app']\n\nfunction token(method: Method, r: Result): Token {\n // CANCEL is the buyer changing their mind. It is not a failure and must never\n // be shown as one.\n if (String(r.status).toUpperCase() === 'CANCEL') throw new Cancelled(method)\n if (r.status !== 'OK' || !r.token) {\n throw new Error(r.errors?.[0]?.message ?? `${method} could not be completed`)\n }\n return { value: r.token, method, card: r.details?.card }\n}\n\n/**\n * The token that arrives on an EVENT rather than from a return — Cash App Pay\n * and ACH, for the same underlying reason: the buyer leaves the page.\n *\n * One listener per collect(), removed when it settles. Square's own quickstart\n * registers a fresh listener on every submission and never removes one, so a\n * buyer who retries gets their token delivered to every previous attempt too.\n */\nfunction awaited(method: Method, t: Tokenizer): Promise<Token> {\n return new Promise<Token>((resolve, reject) => {\n t.addEventListener?.('ontokenization', (e: unknown) => {\n const detail = (e as { detail?: { tokenResult?: Result; error?: unknown } })?.detail\n if (detail?.error) {\n reject(new Error(`${method} could not be completed`))\n return\n }\n if (!detail?.tokenResult) return\n try {\n resolve(token(method, detail.tokenResult))\n } catch (err) {\n reject(err)\n }\n })\n })\n}\n\nclass Web implements Terminal {\n private readonly built = new Map<Method, Tokenizer>()\n private readonly palette: Palette\n\n constructor(\n private readonly payments: Payments,\n palette: Palette,\n ) {\n this.palette = palette\n }\n\n /**\n * Which rails can REALLY pay here, asked of the SDK one at a time.\n *\n * Building the rail IS the probe, and it is the only answer that accounts for\n * the browser, the device, the buyer's saved cards AND whether the merchant\n * account has the rail switched on. A rejection is a plain \"not here\", not an\n * error worth showing anyone.\n *\n * The built objects are KEPT, and that is load-bearing rather than a cache:\n * `collect('apple_pay')` must reach `tokenize()` with nothing awaited in front\n * of it, so the object it needs has to already exist by then.\n */\n async offers(tender: Tender): Promise<Method[]> {\n const out: Method[] = []\n for (const method of REACH.web) {\n try {\n this.built.set(method, await this.build(method, tender))\n out.push(method)\n } catch {\n // Not offerable here. Say nothing and show nothing.\n }\n }\n return out\n }\n\n private async build(method: Method, tender: Tender): Promise<Tokenizer> {\n if (method === 'card') return this.payments.card({ style: style(this.palette) })\n if (method === 'gift') return this.payments.giftCard()\n if (method === 'ach') {\n return this.payments.ach({\n // Square REJECTS a redirectURI carrying a query string, so the page's own\n // path is used and anything that must survive the trip travels separately.\n redirectURI:\n typeof window === 'undefined'\n ? ''\n : window.location.origin + window.location.pathname,\n transactionId: `${Date.now()}`,\n })\n }\n // A FRESH paymentRequest per wallet: Square binds it into the object it\n // builds, so two wallets sharing one request is one wallet quoting the\n // other's total.\n const req = this.payments.paymentRequest({\n countryCode: tender.country,\n currencyCode: tender.total.currency,\n total: { amount: tender.total.amount, label: tender.label },\n })\n if (method === 'apple_pay') return this.payments.applePay(req)\n if (method === 'google_pay') return this.payments.googlePay(req)\n return this.payments.cashAppPay(req, {\n // Where Cash App returns a MOBILE buyer. Desktop uses the QR and never\n // leaves the page.\n redirectURL: typeof window === 'undefined' ? '' : window.location.href,\n referenceId: `pay-${tender.total.amount}`,\n })\n }\n\n /** Draw a rail that renders inline. A rail that draws nothing is a no-op, not an error. */\n async mount(method: Method, target: string): Promise<void> {\n const t = this.built.get(method)\n if (!t) throw new Error(`${method} was not offered here`)\n if (!DRAWN.includes(method)) return\n await t.attach?.(target)\n }\n\n /**\n * Tokenize.\n *\n * NOTHING IS AWAITED BEFORE `tokenize()`. Apple refuses a payment sheet that\n * was not opened directly by the gesture that asked for it, so a single `await`\n * placed above the call — loading the SDK, looking a rail up asynchronously,\n * re-reading a total — silently breaks Apple Pay and nothing else. That is why\n * `offers()` builds every rail up front and this only reads a map.\n */\n collect(method: Method, tender: Tender, detail?: Detail): Promise<Token> {\n const t = this.built.get(method)\n if (!t) return Promise.reject(new Error(`${method} was not offered here`))\n\n // Cash App draws its own button and delivers on the event; a tokenize() call\n // here does nothing at all.\n if (method === 'cash_app') return awaited(method, t)\n\n if (method === 'ach') {\n if (!detail?.name) {\n return Promise.reject(new Error('ACH needs the account holder’s name'))\n }\n const arrived = awaited(method, t)\n // The flow IS tokenize() for ACH — it opens the bank login — but the token\n // comes back on the event, so the return value is deliberately dropped.\n void t.tokenize({\n accountHolderName: detail.name,\n intent: 'CHARGE',\n amount: tender.total.amount,\n currency: tender.total.currency,\n })\n return arrived\n }\n\n return t.tokenize(detail?.verify).then((r) => token(method, r))\n }\n\n async release(): Promise<void> {\n // Square's `destroy()` empties the mount node when it resolves, so a create\n // that overlaps a destroy gets its fresh iframe swept away by the old\n // element's cleanup — the form then sits empty forever with no error to\n // explain it. Callers await this before building the next terminal, which is\n // what makes a theme change survivable.\n await Promise.all([...this.built.values()].map((t) => t.destroy?.().catch(() => undefined)))\n this.built.clear()\n }\n}\n\n/**\n * A terminal for this browser. Loads the SDK once per page, whichever surface\n * asks first.\n *\n * The palette is fixed at construction because SQUARE HAS NO API TO RESTYLE A\n * LIVE CARD — the fields are a cross-origin iframe, told their colours once, at\n * creation. Following a theme change therefore means `release()` then a new\n * terminal, and making the palette a constructor argument is what forces that to\n * happen by construction rather than by remembering to.\n */\nexport async function terminal(config: Config): Promise<Terminal> {\n if (!config.applicationId || !config.locationId) {\n throw new Error('Square is not configured for this deployment')\n }\n const palette = config.palette ?? PALETTE.dark\n // BEFORE the SDK loads, let alone attaches. The container is styled as the\n // card mounts, so these rules have to already be in the sheet by then — see\n // `pin()`. Doing it here is what makes it impossible to ship the style object\n // without the rules that make it visible.\n pin(palette)\n const src = (config.environment ?? 'production').toLowerCase() === 'sandbox' ? SANDBOX : PRODUCTION\n await load(src)\n if (typeof window === 'undefined' || !window.Square) {\n throw new Error('Square SDK failed to load')\n }\n const payments = await window.Square.payments(config.applicationId, config.locationId)\n return new Web(payments, palette)\n}\n"]}