@payment-kit-js/vanilla 0.3.0-alpha.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1979 @@
1
+ /**
2
+ * PaymentKit.js v0.3.0
3
+ * https://paymentkit.com
4
+ *
5
+ * @license MIT
6
+ */
7
+
8
+ "use strict";
9
+ var PaymentKit = (() => {
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __typeError = (msg) => {
15
+ throw TypeError(msg);
16
+ };
17
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
18
+ var __export = (target, all) => {
19
+ for (var name in all)
20
+ __defProp(target, name, { get: all[name], enumerable: true });
21
+ };
22
+ var __copyProps = (to, from, except, desc) => {
23
+ if (from && typeof from === "object" || typeof from === "function") {
24
+ for (let key of __getOwnPropNames(from))
25
+ if (!__hasOwnProp.call(to, key) && key !== except)
26
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
27
+ }
28
+ return to;
29
+ };
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
32
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
33
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
34
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
35
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
36
+
37
+ // src/cdn.ts
38
+ var cdn_exports = {};
39
+ __export(cdn_exports, {
40
+ PaymentMethods: () => PaymentMethods,
41
+ default: () => cdn_default
42
+ });
43
+
44
+ // package.json
45
+ var version = "0.3.0";
46
+
47
+ // ../../node_modules/penpal/dist/penpal.mjs
48
+ var PenpalError = class extends Error {
49
+ constructor(code, message) {
50
+ super(message);
51
+ __publicField(this, "code");
52
+ this.name = "PenpalError";
53
+ this.code = code;
54
+ }
55
+ };
56
+ var PenpalError_default = PenpalError;
57
+ var serializeError = (error) => ({
58
+ name: error.name,
59
+ message: error.message,
60
+ stack: error.stack,
61
+ penpalCode: error instanceof PenpalError_default ? error.code : void 0
62
+ });
63
+ var deserializeError = ({
64
+ name,
65
+ message,
66
+ stack,
67
+ penpalCode
68
+ }) => {
69
+ const deserializedError = penpalCode ? new PenpalError_default(penpalCode, message) : new Error(message);
70
+ deserializedError.name = name;
71
+ deserializedError.stack = stack;
72
+ return deserializedError;
73
+ };
74
+ var brand = Symbol("Reply");
75
+ var _brand, _a;
76
+ var Reply = (_a = class {
77
+ constructor(value, options) {
78
+ __publicField(this, "value");
79
+ __publicField(this, "transferables");
80
+ // Allows TypeScript to distinguish between an actual instance of this
81
+ // class versus an object that looks structurally similar.
82
+ // eslint-disable-next-line no-unused-private-class-members
83
+ __privateAdd(this, _brand, brand);
84
+ this.value = value;
85
+ this.transferables = options?.transferables;
86
+ }
87
+ }, _brand = new WeakMap(), _a);
88
+ var Reply_default = Reply;
89
+ var namespace_default = "penpal";
90
+ var isObject = (value) => {
91
+ return typeof value === "object" && value !== null;
92
+ };
93
+ var isFunction = (value) => {
94
+ return typeof value === "function";
95
+ };
96
+ var isMessage = (data) => {
97
+ return isObject(data) && data.namespace === namespace_default;
98
+ };
99
+ var isSynMessage = (message) => {
100
+ return message.type === "SYN";
101
+ };
102
+ var isAck1Message = (message) => {
103
+ return message.type === "ACK1";
104
+ };
105
+ var isAck2Message = (message) => {
106
+ return message.type === "ACK2";
107
+ };
108
+ var isCallMessage = (message) => {
109
+ return message.type === "CALL";
110
+ };
111
+ var isReplyMessage = (message) => {
112
+ return message.type === "REPLY";
113
+ };
114
+ var isDestroyMessage = (message) => {
115
+ return message.type === "DESTROY";
116
+ };
117
+ var extractMethodPathsFromMethods = (methods, currentPath = []) => {
118
+ const methodPaths = [];
119
+ for (const key of Object.keys(methods)) {
120
+ const value = methods[key];
121
+ if (isFunction(value)) {
122
+ methodPaths.push([...currentPath, key]);
123
+ } else if (isObject(value)) {
124
+ methodPaths.push(
125
+ ...extractMethodPathsFromMethods(value, [...currentPath, key])
126
+ );
127
+ }
128
+ }
129
+ return methodPaths;
130
+ };
131
+ var getMethodAtMethodPath = (methodPath, methods) => {
132
+ const result = methodPath.reduce(
133
+ (acc, pathSegment) => {
134
+ return isObject(acc) ? acc[pathSegment] : void 0;
135
+ },
136
+ methods
137
+ );
138
+ return isFunction(result) ? result : void 0;
139
+ };
140
+ var formatMethodPath = (methodPath) => {
141
+ return methodPath.join(".");
142
+ };
143
+ var createErrorReplyMessage = (channel, callId, error) => ({
144
+ namespace: namespace_default,
145
+ channel,
146
+ type: "REPLY",
147
+ callId,
148
+ isError: true,
149
+ ...error instanceof Error ? { value: serializeError(error), isSerializedErrorInstance: true } : { value: error }
150
+ });
151
+ var connectCallHandler = (messenger, methods, channel, log) => {
152
+ let isDestroyed = false;
153
+ const handleMessage = async (message) => {
154
+ if (isDestroyed) {
155
+ return;
156
+ }
157
+ if (!isCallMessage(message)) {
158
+ return;
159
+ }
160
+ log?.(`Received ${formatMethodPath(message.methodPath)}() call`, message);
161
+ const { methodPath, args, id: callId } = message;
162
+ let replyMessage;
163
+ let transferables;
164
+ try {
165
+ const method = getMethodAtMethodPath(methodPath, methods);
166
+ if (!method) {
167
+ throw new PenpalError_default(
168
+ "METHOD_NOT_FOUND",
169
+ `Method \`${formatMethodPath(methodPath)}\` is not found.`
170
+ );
171
+ }
172
+ let value = await method(...args);
173
+ if (value instanceof Reply_default) {
174
+ transferables = value.transferables;
175
+ value = await value.value;
176
+ }
177
+ replyMessage = {
178
+ namespace: namespace_default,
179
+ channel,
180
+ type: "REPLY",
181
+ callId,
182
+ value
183
+ };
184
+ } catch (error) {
185
+ replyMessage = createErrorReplyMessage(channel, callId, error);
186
+ }
187
+ if (isDestroyed) {
188
+ return;
189
+ }
190
+ try {
191
+ log?.(`Sending ${formatMethodPath(methodPath)}() reply`, replyMessage);
192
+ messenger.sendMessage(replyMessage, transferables);
193
+ } catch (error) {
194
+ if (error.name === "DataCloneError") {
195
+ replyMessage = createErrorReplyMessage(channel, callId, error);
196
+ log?.(`Sending ${formatMethodPath(methodPath)}() reply`, replyMessage);
197
+ messenger.sendMessage(replyMessage);
198
+ }
199
+ throw error;
200
+ }
201
+ };
202
+ messenger.addMessageHandler(handleMessage);
203
+ return () => {
204
+ isDestroyed = true;
205
+ messenger.removeMessageHandler(handleMessage);
206
+ };
207
+ };
208
+ var connectCallHandler_default = connectCallHandler;
209
+ var generateId_default = crypto.randomUUID?.bind(crypto) ?? (() => new Array(4).fill(0).map(
210
+ () => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)
211
+ ).join("-"));
212
+ var brand2 = Symbol("CallOptions");
213
+ var _brand2, _a2;
214
+ var CallOptions = (_a2 = class {
215
+ constructor(options) {
216
+ __publicField(this, "transferables");
217
+ __publicField(this, "timeout");
218
+ // Allows TypeScript to distinguish between an actual instance of this
219
+ // class versus an object that looks structurally similar.
220
+ // eslint-disable-next-line no-unused-private-class-members
221
+ __privateAdd(this, _brand2, brand2);
222
+ this.transferables = options?.transferables;
223
+ this.timeout = options?.timeout;
224
+ }
225
+ }, _brand2 = new WeakMap(), _a2);
226
+ var CallOptions_default = CallOptions;
227
+ var methodsToTreatAsNative = /* @__PURE__ */ new Set(["apply", "call", "bind"]);
228
+ var createRemoteProxy = (callback, log, path = []) => {
229
+ return new Proxy(
230
+ path.length ? () => {
231
+ } : /* @__PURE__ */ Object.create(null),
232
+ {
233
+ get(target, prop) {
234
+ if (prop === "then") {
235
+ return;
236
+ }
237
+ if (path.length && methodsToTreatAsNative.has(prop)) {
238
+ return Reflect.get(target, prop);
239
+ }
240
+ return createRemoteProxy(callback, log, [...path, prop]);
241
+ },
242
+ apply(target, _thisArg, args) {
243
+ return callback(path, args);
244
+ }
245
+ }
246
+ );
247
+ };
248
+ var getDestroyedConnectionMethodCallError = (methodPath) => {
249
+ return new PenpalError_default(
250
+ "CONNECTION_DESTROYED",
251
+ `Method call ${formatMethodPath(
252
+ methodPath
253
+ )}() failed due to destroyed connection`
254
+ );
255
+ };
256
+ var connectRemoteProxy = (messenger, channel, log) => {
257
+ let isDestroyed = false;
258
+ const replyHandlers = /* @__PURE__ */ new Map();
259
+ const handleMessage = (message) => {
260
+ if (!isReplyMessage(message)) {
261
+ return;
262
+ }
263
+ const { callId, value, isError, isSerializedErrorInstance } = message;
264
+ const replyHandler = replyHandlers.get(callId);
265
+ if (!replyHandler) {
266
+ return;
267
+ }
268
+ replyHandlers.delete(callId);
269
+ log?.(
270
+ `Received ${formatMethodPath(replyHandler.methodPath)}() call`,
271
+ message
272
+ );
273
+ if (isError) {
274
+ replyHandler.reject(
275
+ isSerializedErrorInstance ? deserializeError(value) : value
276
+ );
277
+ } else {
278
+ replyHandler.resolve(value);
279
+ }
280
+ };
281
+ messenger.addMessageHandler(handleMessage);
282
+ const remoteProxy = createRemoteProxy((methodPath, args) => {
283
+ if (isDestroyed) {
284
+ throw getDestroyedConnectionMethodCallError(methodPath);
285
+ }
286
+ const callId = generateId_default();
287
+ const lastArg = args[args.length - 1];
288
+ const lastArgIsOptions = lastArg instanceof CallOptions_default;
289
+ const { timeout, transferables } = lastArgIsOptions ? lastArg : {};
290
+ const argsWithoutOptions = lastArgIsOptions ? args.slice(0, -1) : args;
291
+ return new Promise((resolve, reject) => {
292
+ const timeoutId = timeout !== void 0 ? window.setTimeout(() => {
293
+ replyHandlers.delete(callId);
294
+ reject(
295
+ new PenpalError_default(
296
+ "METHOD_CALL_TIMEOUT",
297
+ `Method call ${formatMethodPath(
298
+ methodPath
299
+ )}() timed out after ${timeout}ms`
300
+ )
301
+ );
302
+ }, timeout) : void 0;
303
+ replyHandlers.set(callId, { methodPath, resolve, reject, timeoutId });
304
+ try {
305
+ const callMessage = {
306
+ namespace: namespace_default,
307
+ channel,
308
+ type: "CALL",
309
+ id: callId,
310
+ methodPath,
311
+ args: argsWithoutOptions
312
+ };
313
+ log?.(`Sending ${formatMethodPath(methodPath)}() call`, callMessage);
314
+ messenger.sendMessage(callMessage, transferables);
315
+ } catch (error) {
316
+ reject(
317
+ new PenpalError_default("TRANSMISSION_FAILED", error.message)
318
+ );
319
+ }
320
+ });
321
+ }, log);
322
+ const destroy = () => {
323
+ isDestroyed = true;
324
+ messenger.removeMessageHandler(handleMessage);
325
+ for (const { methodPath, reject, timeoutId } of replyHandlers.values()) {
326
+ clearTimeout(timeoutId);
327
+ reject(getDestroyedConnectionMethodCallError(methodPath));
328
+ }
329
+ replyHandlers.clear();
330
+ };
331
+ return {
332
+ remoteProxy,
333
+ destroy
334
+ };
335
+ };
336
+ var connectRemoteProxy_default = connectRemoteProxy;
337
+ var getPromiseWithResolvers = () => {
338
+ let resolve;
339
+ let reject;
340
+ const promise = new Promise((res, rej) => {
341
+ resolve = res;
342
+ reject = rej;
343
+ });
344
+ return {
345
+ promise,
346
+ resolve,
347
+ reject
348
+ };
349
+ };
350
+ var getPromiseWithResolvers_default = getPromiseWithResolvers;
351
+ var PenpalBugError = class extends Error {
352
+ constructor(message) {
353
+ super(
354
+ `You've hit a bug in Penpal. Please file an issue with the following information: ${message}`
355
+ );
356
+ }
357
+ };
358
+ var PenpalBugError_default = PenpalBugError;
359
+ var DEPRECATED_PENPAL_PARTICIPANT_ID = "deprecated-penpal";
360
+ var isDeprecatedMessage = (data) => {
361
+ return isObject(data) && "penpal" in data;
362
+ };
363
+ var upgradeMethodPath = (methodPath) => methodPath.split(".");
364
+ var downgradeMethodPath = (methodPath) => methodPath.join(".");
365
+ var getUnexpectedMessageError = (message) => {
366
+ return new PenpalBugError_default(
367
+ `Unexpected message to translate: ${JSON.stringify(message)}`
368
+ );
369
+ };
370
+ var upgradeMessage = (message) => {
371
+ if (message.penpal === "syn") {
372
+ return {
373
+ namespace: namespace_default,
374
+ channel: void 0,
375
+ type: "SYN",
376
+ participantId: DEPRECATED_PENPAL_PARTICIPANT_ID
377
+ };
378
+ }
379
+ if (message.penpal === "ack") {
380
+ return {
381
+ namespace: namespace_default,
382
+ channel: void 0,
383
+ type: "ACK2"
384
+ };
385
+ }
386
+ if (message.penpal === "call") {
387
+ return {
388
+ namespace: namespace_default,
389
+ channel: void 0,
390
+ type: "CALL",
391
+ // Actually converting the ID to a string would break communication.
392
+ id: message.id,
393
+ methodPath: upgradeMethodPath(message.methodName),
394
+ args: message.args
395
+ };
396
+ }
397
+ if (message.penpal === "reply") {
398
+ if (message.resolution === "fulfilled") {
399
+ return {
400
+ namespace: namespace_default,
401
+ channel: void 0,
402
+ type: "REPLY",
403
+ // Actually converting the ID to a string would break communication.
404
+ callId: message.id,
405
+ value: message.returnValue
406
+ };
407
+ } else {
408
+ return {
409
+ namespace: namespace_default,
410
+ channel: void 0,
411
+ type: "REPLY",
412
+ // Actually converting the ID to a string would break communication.
413
+ callId: message.id,
414
+ isError: true,
415
+ ...message.returnValueIsError ? {
416
+ value: message.returnValue,
417
+ isSerializedErrorInstance: true
418
+ } : {
419
+ value: message.returnValue
420
+ }
421
+ };
422
+ }
423
+ }
424
+ throw getUnexpectedMessageError(message);
425
+ };
426
+ var downgradeMessage = (message) => {
427
+ if (isAck1Message(message)) {
428
+ return {
429
+ penpal: "synAck",
430
+ methodNames: message.methodPaths.map(downgradeMethodPath)
431
+ };
432
+ }
433
+ if (isCallMessage(message)) {
434
+ return {
435
+ penpal: "call",
436
+ // Actually converting the ID to a number would break communication.
437
+ id: message.id,
438
+ methodName: downgradeMethodPath(message.methodPath),
439
+ args: message.args
440
+ };
441
+ }
442
+ if (isReplyMessage(message)) {
443
+ if (message.isError) {
444
+ return {
445
+ penpal: "reply",
446
+ // Actually converting the ID to a number would break communication.
447
+ id: message.callId,
448
+ resolution: "rejected",
449
+ ...message.isSerializedErrorInstance ? {
450
+ returnValue: message.value,
451
+ returnValueIsError: true
452
+ } : { returnValue: message.value }
453
+ };
454
+ } else {
455
+ return {
456
+ penpal: "reply",
457
+ // Actually converting the ID to a number would break communication.
458
+ id: message.callId,
459
+ resolution: "fulfilled",
460
+ returnValue: message.value
461
+ };
462
+ }
463
+ }
464
+ throw getUnexpectedMessageError(message);
465
+ };
466
+ var shakeHands = ({
467
+ messenger,
468
+ methods,
469
+ timeout,
470
+ channel,
471
+ log
472
+ }) => {
473
+ const participantId = generateId_default();
474
+ let remoteParticipantId;
475
+ const destroyHandlers = [];
476
+ let isComplete = false;
477
+ const methodPaths = extractMethodPathsFromMethods(methods);
478
+ const { promise, resolve, reject } = getPromiseWithResolvers_default();
479
+ const timeoutId = timeout !== void 0 ? setTimeout(() => {
480
+ reject(
481
+ new PenpalError_default(
482
+ "CONNECTION_TIMEOUT",
483
+ `Connection timed out after ${timeout}ms`
484
+ )
485
+ );
486
+ }, timeout) : void 0;
487
+ const destroy = () => {
488
+ for (const destroyHandler of destroyHandlers) {
489
+ destroyHandler();
490
+ }
491
+ };
492
+ const connectCallHandlerAndMethodProxies = () => {
493
+ if (isComplete) {
494
+ return;
495
+ }
496
+ destroyHandlers.push(connectCallHandler_default(messenger, methods, channel, log));
497
+ const { remoteProxy, destroy: destroyMethodProxies } = connectRemoteProxy_default(messenger, channel, log);
498
+ destroyHandlers.push(destroyMethodProxies);
499
+ clearTimeout(timeoutId);
500
+ isComplete = true;
501
+ resolve({
502
+ remoteProxy,
503
+ destroy
504
+ });
505
+ };
506
+ const sendSynMessage = () => {
507
+ const synMessage = {
508
+ namespace: namespace_default,
509
+ type: "SYN",
510
+ channel,
511
+ participantId
512
+ };
513
+ log?.(`Sending handshake SYN`, synMessage);
514
+ try {
515
+ messenger.sendMessage(synMessage);
516
+ } catch (error) {
517
+ reject(new PenpalError_default("TRANSMISSION_FAILED", error.message));
518
+ }
519
+ };
520
+ const handleSynMessage = (message) => {
521
+ log?.(`Received handshake SYN`, message);
522
+ if (message.participantId === remoteParticipantId && // TODO: Used for backward-compatibility. Remove in next major version.
523
+ remoteParticipantId !== DEPRECATED_PENPAL_PARTICIPANT_ID) {
524
+ return;
525
+ }
526
+ remoteParticipantId = message.participantId;
527
+ sendSynMessage();
528
+ const isHandshakeLeader = participantId > remoteParticipantId || // TODO: Used for backward-compatibility. Remove in next major version.
529
+ remoteParticipantId === DEPRECATED_PENPAL_PARTICIPANT_ID;
530
+ if (!isHandshakeLeader) {
531
+ return;
532
+ }
533
+ const ack1Message = {
534
+ namespace: namespace_default,
535
+ channel,
536
+ type: "ACK1",
537
+ methodPaths
538
+ };
539
+ log?.(`Sending handshake ACK1`, ack1Message);
540
+ try {
541
+ messenger.sendMessage(ack1Message);
542
+ } catch (error) {
543
+ reject(new PenpalError_default("TRANSMISSION_FAILED", error.message));
544
+ return;
545
+ }
546
+ };
547
+ const handleAck1Message = (message) => {
548
+ log?.(`Received handshake ACK1`, message);
549
+ const ack2Message = {
550
+ namespace: namespace_default,
551
+ channel,
552
+ type: "ACK2"
553
+ };
554
+ log?.(`Sending handshake ACK2`, ack2Message);
555
+ try {
556
+ messenger.sendMessage(ack2Message);
557
+ } catch (error) {
558
+ reject(new PenpalError_default("TRANSMISSION_FAILED", error.message));
559
+ return;
560
+ }
561
+ connectCallHandlerAndMethodProxies();
562
+ };
563
+ const handleAck2Message = (message) => {
564
+ log?.(`Received handshake ACK2`, message);
565
+ connectCallHandlerAndMethodProxies();
566
+ };
567
+ const handleMessage = (message) => {
568
+ if (isSynMessage(message)) {
569
+ handleSynMessage(message);
570
+ }
571
+ if (isAck1Message(message)) {
572
+ handleAck1Message(message);
573
+ }
574
+ if (isAck2Message(message)) {
575
+ handleAck2Message(message);
576
+ }
577
+ };
578
+ messenger.addMessageHandler(handleMessage);
579
+ destroyHandlers.push(() => messenger.removeMessageHandler(handleMessage));
580
+ sendSynMessage();
581
+ return promise;
582
+ };
583
+ var shakeHands_default = shakeHands;
584
+ var once = (fn) => {
585
+ let isCalled = false;
586
+ let result;
587
+ return (...args) => {
588
+ if (!isCalled) {
589
+ isCalled = true;
590
+ result = fn(...args);
591
+ }
592
+ return result;
593
+ };
594
+ };
595
+ var once_default = once;
596
+ var usedMessengers = /* @__PURE__ */ new WeakSet();
597
+ var connect = ({
598
+ messenger,
599
+ methods = {},
600
+ timeout,
601
+ channel,
602
+ log
603
+ }) => {
604
+ if (!messenger) {
605
+ throw new PenpalError_default("INVALID_ARGUMENT", "messenger must be defined");
606
+ }
607
+ if (usedMessengers.has(messenger)) {
608
+ throw new PenpalError_default(
609
+ "INVALID_ARGUMENT",
610
+ "A messenger can only be used for a single connection"
611
+ );
612
+ }
613
+ usedMessengers.add(messenger);
614
+ const connectionDestroyedHandlers = [messenger.destroy];
615
+ const destroyConnection = once_default((notifyOtherParticipant) => {
616
+ if (notifyOtherParticipant) {
617
+ const destroyMessage = {
618
+ namespace: namespace_default,
619
+ channel,
620
+ type: "DESTROY"
621
+ };
622
+ try {
623
+ messenger.sendMessage(destroyMessage);
624
+ } catch (_) {
625
+ }
626
+ }
627
+ for (const connectionDestroyedHandler of connectionDestroyedHandlers) {
628
+ connectionDestroyedHandler();
629
+ }
630
+ log?.("Connection destroyed");
631
+ });
632
+ const validateReceivedMessage = (data) => {
633
+ return isMessage(data) && data.channel === channel;
634
+ };
635
+ const promise = (async () => {
636
+ try {
637
+ messenger.initialize({ log, validateReceivedMessage });
638
+ messenger.addMessageHandler((message) => {
639
+ if (isDestroyMessage(message)) {
640
+ destroyConnection(false);
641
+ }
642
+ });
643
+ const { remoteProxy, destroy } = await shakeHands_default({
644
+ messenger,
645
+ methods,
646
+ timeout,
647
+ channel,
648
+ log
649
+ });
650
+ connectionDestroyedHandlers.push(destroy);
651
+ return remoteProxy;
652
+ } catch (error) {
653
+ destroyConnection(true);
654
+ throw error;
655
+ }
656
+ })();
657
+ return {
658
+ promise,
659
+ // Why we don't reject the connection promise when consumer calls destroy():
660
+ // https://github.com/Aaronius/penpal/issues/51
661
+ destroy: () => {
662
+ destroyConnection(true);
663
+ }
664
+ };
665
+ };
666
+ var connect_default = connect;
667
+ var _remoteWindow, _allowedOrigins, _log, _validateReceivedMessage, _concreteRemoteOrigin, _messageCallbacks, _port, _isChildUsingDeprecatedProtocol, _isAllowedOrigin, _getOriginForSendingMessage, _destroyPort, _handleMessageFromRemoteWindow, _handleMessageFromPort, _a3;
668
+ var WindowMessenger = (_a3 = class {
669
+ constructor({ remoteWindow, allowedOrigins }) {
670
+ __privateAdd(this, _remoteWindow);
671
+ __privateAdd(this, _allowedOrigins);
672
+ __privateAdd(this, _log);
673
+ __privateAdd(this, _validateReceivedMessage);
674
+ __privateAdd(this, _concreteRemoteOrigin);
675
+ __privateAdd(this, _messageCallbacks, /* @__PURE__ */ new Set());
676
+ __privateAdd(this, _port);
677
+ // TODO: Used for backward-compatibility. Remove in next major version.
678
+ __privateAdd(this, _isChildUsingDeprecatedProtocol, false);
679
+ __publicField(this, "initialize", ({
680
+ log,
681
+ validateReceivedMessage
682
+ }) => {
683
+ __privateSet(this, _log, log);
684
+ __privateSet(this, _validateReceivedMessage, validateReceivedMessage);
685
+ window.addEventListener("message", __privateGet(this, _handleMessageFromRemoteWindow));
686
+ });
687
+ __publicField(this, "sendMessage", (message, transferables) => {
688
+ if (isSynMessage(message)) {
689
+ const originForSending = __privateGet(this, _getOriginForSendingMessage).call(this, message);
690
+ __privateGet(this, _remoteWindow).postMessage(message, {
691
+ targetOrigin: originForSending,
692
+ transfer: transferables
693
+ });
694
+ return;
695
+ }
696
+ if (isAck1Message(message) || // If the child is using a previous version of Penpal, we need to
697
+ // downgrade the message and send it through the window rather than
698
+ // the port because older versions of Penpal don't use MessagePorts.
699
+ __privateGet(this, _isChildUsingDeprecatedProtocol)) {
700
+ const payload = __privateGet(this, _isChildUsingDeprecatedProtocol) ? downgradeMessage(message) : message;
701
+ const originForSending = __privateGet(this, _getOriginForSendingMessage).call(this, message);
702
+ __privateGet(this, _remoteWindow).postMessage(payload, {
703
+ targetOrigin: originForSending,
704
+ transfer: transferables
705
+ });
706
+ return;
707
+ }
708
+ if (isAck2Message(message)) {
709
+ const { port1, port2 } = new MessageChannel();
710
+ __privateSet(this, _port, port1);
711
+ port1.addEventListener("message", __privateGet(this, _handleMessageFromPort));
712
+ port1.start();
713
+ const transferablesToSend = [port2, ...transferables || []];
714
+ const originForSending = __privateGet(this, _getOriginForSendingMessage).call(this, message);
715
+ __privateGet(this, _remoteWindow).postMessage(message, {
716
+ targetOrigin: originForSending,
717
+ transfer: transferablesToSend
718
+ });
719
+ return;
720
+ }
721
+ if (__privateGet(this, _port)) {
722
+ __privateGet(this, _port).postMessage(message, {
723
+ transfer: transferables
724
+ });
725
+ return;
726
+ }
727
+ throw new PenpalBugError_default("Port is undefined");
728
+ });
729
+ __publicField(this, "addMessageHandler", (callback) => {
730
+ __privateGet(this, _messageCallbacks).add(callback);
731
+ });
732
+ __publicField(this, "removeMessageHandler", (callback) => {
733
+ __privateGet(this, _messageCallbacks).delete(callback);
734
+ });
735
+ __publicField(this, "destroy", () => {
736
+ window.removeEventListener("message", __privateGet(this, _handleMessageFromRemoteWindow));
737
+ __privateGet(this, _destroyPort).call(this);
738
+ __privateGet(this, _messageCallbacks).clear();
739
+ });
740
+ __privateAdd(this, _isAllowedOrigin, (origin) => {
741
+ return __privateGet(this, _allowedOrigins).some(
742
+ (allowedOrigin) => allowedOrigin instanceof RegExp ? allowedOrigin.test(origin) : allowedOrigin === origin || allowedOrigin === "*"
743
+ );
744
+ });
745
+ __privateAdd(this, _getOriginForSendingMessage, (message) => {
746
+ if (isSynMessage(message)) {
747
+ return "*";
748
+ }
749
+ if (!__privateGet(this, _concreteRemoteOrigin)) {
750
+ throw new PenpalBugError_default("Concrete remote origin not set");
751
+ }
752
+ return __privateGet(this, _concreteRemoteOrigin) === "null" && __privateGet(this, _allowedOrigins).includes("*") ? "*" : __privateGet(this, _concreteRemoteOrigin);
753
+ });
754
+ __privateAdd(this, _destroyPort, () => {
755
+ __privateGet(this, _port)?.removeEventListener("message", __privateGet(this, _handleMessageFromPort));
756
+ __privateGet(this, _port)?.close();
757
+ __privateSet(this, _port, void 0);
758
+ });
759
+ __privateAdd(this, _handleMessageFromRemoteWindow, ({
760
+ source,
761
+ origin,
762
+ ports,
763
+ data
764
+ }) => {
765
+ var _a4, _b, _c;
766
+ if (source !== __privateGet(this, _remoteWindow)) {
767
+ return;
768
+ }
769
+ if (isDeprecatedMessage(data)) {
770
+ (_a4 = __privateGet(this, _log)) == null ? void 0 : _a4.call(
771
+ this,
772
+ "Please upgrade the child window to the latest version of Penpal."
773
+ );
774
+ __privateSet(this, _isChildUsingDeprecatedProtocol, true);
775
+ data = upgradeMessage(data);
776
+ }
777
+ if (!((_b = __privateGet(this, _validateReceivedMessage)) == null ? void 0 : _b.call(this, data))) {
778
+ return;
779
+ }
780
+ if (!__privateGet(this, _isAllowedOrigin).call(this, origin)) {
781
+ (_c = __privateGet(this, _log)) == null ? void 0 : _c.call(
782
+ this,
783
+ `Received a message from origin \`${origin}\` which did not match allowed origins \`[${__privateGet(this, _allowedOrigins).join(", ")}]\``
784
+ );
785
+ return;
786
+ }
787
+ if (isSynMessage(data)) {
788
+ __privateGet(this, _destroyPort).call(this);
789
+ __privateSet(this, _concreteRemoteOrigin, origin);
790
+ }
791
+ if (isAck2Message(data) && // Previous versions of Penpal don't use MessagePorts and do all
792
+ // communication through the window.
793
+ !__privateGet(this, _isChildUsingDeprecatedProtocol)) {
794
+ __privateSet(this, _port, ports[0]);
795
+ if (!__privateGet(this, _port)) {
796
+ throw new PenpalBugError_default("No port received on ACK2");
797
+ }
798
+ __privateGet(this, _port).addEventListener("message", __privateGet(this, _handleMessageFromPort));
799
+ __privateGet(this, _port).start();
800
+ }
801
+ for (const callback of __privateGet(this, _messageCallbacks)) {
802
+ callback(data);
803
+ }
804
+ });
805
+ __privateAdd(this, _handleMessageFromPort, ({ data }) => {
806
+ var _a4;
807
+ if (!((_a4 = __privateGet(this, _validateReceivedMessage)) == null ? void 0 : _a4.call(this, data))) {
808
+ return;
809
+ }
810
+ for (const callback of __privateGet(this, _messageCallbacks)) {
811
+ callback(data);
812
+ }
813
+ });
814
+ if (!remoteWindow) {
815
+ throw new PenpalError_default("INVALID_ARGUMENT", "remoteWindow must be defined");
816
+ }
817
+ __privateSet(this, _remoteWindow, remoteWindow);
818
+ __privateSet(this, _allowedOrigins, allowedOrigins?.length ? allowedOrigins : [window.origin]);
819
+ }
820
+ }, _remoteWindow = new WeakMap(), _allowedOrigins = new WeakMap(), _log = new WeakMap(), _validateReceivedMessage = new WeakMap(), _concreteRemoteOrigin = new WeakMap(), _messageCallbacks = new WeakMap(), _port = new WeakMap(), _isChildUsingDeprecatedProtocol = new WeakMap(), _isAllowedOrigin = new WeakMap(), _getOriginForSendingMessage = new WeakMap(), _destroyPort = new WeakMap(), _handleMessageFromRemoteWindow = new WeakMap(), _handleMessageFromPort = new WeakMap(), _a3);
821
+ var WindowMessenger_default = WindowMessenger;
822
+
823
+ // src/penpal/index.ts
824
+ var connectToWindow = ({
825
+ window: window2,
826
+ methods
827
+ }) => {
828
+ const messenger = new WindowMessenger_default({
829
+ remoteWindow: window2,
830
+ // Allow all origins since the iframe is loaded from a trusted source (baseUrl)
831
+ // The security boundary is the checkout_token, not the origin
832
+ allowedOrigins: ["*"]
833
+ });
834
+ return connect_default({ methods, messenger });
835
+ };
836
+
837
+ // src/penpal/connect-tunnel-x.ts
838
+ var connectToTunnelXIframe = (iframe, methods) => {
839
+ return connectToWindow({ window: iframe.contentWindow, methods });
840
+ };
841
+ var _TunnelXManager = class _TunnelXManager {
842
+ constructor(penpalConn, penpalMethods) {
843
+ __publicField(this, "penpalConn");
844
+ __publicField(this, "_methods");
845
+ this.penpalConn = penpalConn;
846
+ this._methods = penpalMethods;
847
+ }
848
+ /**
849
+ * Makes publicEndpoints act like a real PK client, but actually forwards all calls
850
+ * through queryPublicEndpoint and sends it through penpal.
851
+ */
852
+ get publicEndpoints() {
853
+ const handler = {
854
+ get: (_target, prop) => {
855
+ return async (...args) => {
856
+ return await this._methods.queryPublicEndpoint(prop, args[0], args[1]);
857
+ };
858
+ }
859
+ };
860
+ return new Proxy({}, handler);
861
+ }
862
+ destroy() {
863
+ this.penpalConn.destroy();
864
+ }
865
+ };
866
+ __publicField(_TunnelXManager, "createFromPenpalConnection", async (penpalConn) => {
867
+ const methods = await penpalConn.promise;
868
+ const manager = new _TunnelXManager(penpalConn, methods);
869
+ return manager;
870
+ });
871
+ var TunnelXManager = _TunnelXManager;
872
+
873
+ // src/types.ts
874
+ var ENVIRONMENT_URLS = {
875
+ local: {
876
+ baseUrl: "http://localhost:9101",
877
+ apiBaseUrl: "http://localhost:9000"
878
+ },
879
+ sandbox: {
880
+ baseUrl: "https://staging.paymentkit.com/customer",
881
+ apiBaseUrl: "https://staging.paymentkit.com"
882
+ },
883
+ production: {
884
+ baseUrl: "https://paymentkit.com/customer",
885
+ apiBaseUrl: "https://paymentkit.com"
886
+ }
887
+ };
888
+ function getUrlsForEnvironment(environment) {
889
+ const env = environment;
890
+ const urls = ENVIRONMENT_URLS[env];
891
+ if (!urls) {
892
+ throw new Error(`Invalid environment: ${environment}. Must be one of: local, sandbox, production`);
893
+ }
894
+ return urls;
895
+ }
896
+
897
+ // ../../node_modules/valibot/dist/index.mjs
898
+ var store$4;
899
+ // @__NO_SIDE_EFFECTS__
900
+ function getGlobalConfig(config$1) {
901
+ return {
902
+ lang: config$1?.lang ?? store$4?.lang,
903
+ message: config$1?.message,
904
+ abortEarly: config$1?.abortEarly ?? store$4?.abortEarly,
905
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$4?.abortPipeEarly
906
+ };
907
+ }
908
+ var store$3;
909
+ // @__NO_SIDE_EFFECTS__
910
+ function getGlobalMessage(lang) {
911
+ return store$3?.get(lang);
912
+ }
913
+ var store$2;
914
+ // @__NO_SIDE_EFFECTS__
915
+ function getSchemaMessage(lang) {
916
+ return store$2?.get(lang);
917
+ }
918
+ var store$1;
919
+ // @__NO_SIDE_EFFECTS__
920
+ function getSpecificMessage(reference, lang) {
921
+ return store$1?.get(reference)?.get(lang);
922
+ }
923
+ // @__NO_SIDE_EFFECTS__
924
+ function _stringify(input) {
925
+ const type = typeof input;
926
+ if (type === "string") return `"${input}"`;
927
+ if (type === "number" || type === "bigint" || type === "boolean") return `${input}`;
928
+ if (type === "object" || type === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
929
+ return type;
930
+ }
931
+ function _addIssue(context, label, dataset, config$1, other) {
932
+ const input = other && "input" in other ? other.input : dataset.value;
933
+ const expected = other?.expected ?? context.expects ?? null;
934
+ const received = other?.received ?? /* @__PURE__ */ _stringify(input);
935
+ const issue = {
936
+ kind: context.kind,
937
+ type: context.type,
938
+ input,
939
+ expected,
940
+ received,
941
+ message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
942
+ requirement: context.requirement,
943
+ path: other?.path,
944
+ issues: other?.issues,
945
+ lang: config$1.lang,
946
+ abortEarly: config$1.abortEarly,
947
+ abortPipeEarly: config$1.abortPipeEarly
948
+ };
949
+ const isSchema = context.kind === "schema";
950
+ const message$1 = other?.message ?? context.message ?? /* @__PURE__ */ getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? /* @__PURE__ */ getSchemaMessage(issue.lang) : null) ?? config$1.message ?? /* @__PURE__ */ getGlobalMessage(issue.lang);
951
+ if (message$1 !== void 0) issue.message = typeof message$1 === "function" ? message$1(issue) : message$1;
952
+ if (isSchema) dataset.typed = false;
953
+ if (dataset.issues) dataset.issues.push(issue);
954
+ else dataset.issues = [issue];
955
+ }
956
+ // @__NO_SIDE_EFFECTS__
957
+ function _getStandardProps(context) {
958
+ return {
959
+ version: 1,
960
+ vendor: "valibot",
961
+ validate(value$1) {
962
+ return context["~run"]({ value: value$1 }, /* @__PURE__ */ getGlobalConfig());
963
+ }
964
+ };
965
+ }
966
+ // @__NO_SIDE_EFFECTS__
967
+ function getDotPath(issue) {
968
+ if (issue.path) {
969
+ let key = "";
970
+ for (const item of issue.path) if (typeof item.key === "string" || typeof item.key === "number") if (key) key += `.${item.key}`;
971
+ else key += item.key;
972
+ else return null;
973
+ return key;
974
+ }
975
+ return null;
976
+ }
977
+ var EMAIL_REGEX = /^[\w+-]+(?:\.[\w+-]+)*@[\da-z]+(?:[.-][\da-z]+)*\.[a-z]{2,}$/iu;
978
+ // @__NO_SIDE_EFFECTS__
979
+ function email(message$1) {
980
+ return {
981
+ kind: "validation",
982
+ type: "email",
983
+ reference: email,
984
+ expects: null,
985
+ async: false,
986
+ requirement: EMAIL_REGEX,
987
+ message: message$1,
988
+ "~run"(dataset, config$1) {
989
+ if (dataset.typed && !this.requirement.test(dataset.value)) _addIssue(this, "email", dataset, config$1);
990
+ return dataset;
991
+ }
992
+ };
993
+ }
994
+ // @__NO_SIDE_EFFECTS__
995
+ function maxLength(requirement, message$1) {
996
+ return {
997
+ kind: "validation",
998
+ type: "max_length",
999
+ reference: maxLength,
1000
+ async: false,
1001
+ expects: `<=${requirement}`,
1002
+ requirement,
1003
+ message: message$1,
1004
+ "~run"(dataset, config$1) {
1005
+ if (dataset.typed && dataset.value.length > this.requirement) _addIssue(this, "length", dataset, config$1, { received: `${dataset.value.length}` });
1006
+ return dataset;
1007
+ }
1008
+ };
1009
+ }
1010
+ // @__NO_SIDE_EFFECTS__
1011
+ function minLength(requirement, message$1) {
1012
+ return {
1013
+ kind: "validation",
1014
+ type: "min_length",
1015
+ reference: minLength,
1016
+ async: false,
1017
+ expects: `>=${requirement}`,
1018
+ requirement,
1019
+ message: message$1,
1020
+ "~run"(dataset, config$1) {
1021
+ if (dataset.typed && dataset.value.length < this.requirement) _addIssue(this, "length", dataset, config$1, { received: `${dataset.value.length}` });
1022
+ return dataset;
1023
+ }
1024
+ };
1025
+ }
1026
+ // @__NO_SIDE_EFFECTS__
1027
+ function nonEmpty(message$1) {
1028
+ return {
1029
+ kind: "validation",
1030
+ type: "non_empty",
1031
+ reference: nonEmpty,
1032
+ async: false,
1033
+ expects: "!0",
1034
+ message: message$1,
1035
+ "~run"(dataset, config$1) {
1036
+ if (dataset.typed && dataset.value.length === 0) _addIssue(this, "length", dataset, config$1, { received: "0" });
1037
+ return dataset;
1038
+ }
1039
+ };
1040
+ }
1041
+ // @__NO_SIDE_EFFECTS__
1042
+ function getFallback(schema, dataset, config$1) {
1043
+ return typeof schema.fallback === "function" ? schema.fallback(dataset, config$1) : schema.fallback;
1044
+ }
1045
+ // @__NO_SIDE_EFFECTS__
1046
+ function getDefault(schema, dataset, config$1) {
1047
+ return typeof schema.default === "function" ? schema.default(dataset, config$1) : schema.default;
1048
+ }
1049
+ // @__NO_SIDE_EFFECTS__
1050
+ function object(entries$1, message$1) {
1051
+ return {
1052
+ kind: "schema",
1053
+ type: "object",
1054
+ reference: object,
1055
+ expects: "Object",
1056
+ async: false,
1057
+ entries: entries$1,
1058
+ message: message$1,
1059
+ get "~standard"() {
1060
+ return /* @__PURE__ */ _getStandardProps(this);
1061
+ },
1062
+ "~run"(dataset, config$1) {
1063
+ const input = dataset.value;
1064
+ if (input && typeof input === "object") {
1065
+ dataset.typed = true;
1066
+ dataset.value = {};
1067
+ for (const key in this.entries) {
1068
+ const valueSchema = this.entries[key];
1069
+ if (key in input || (valueSchema.type === "exact_optional" || valueSchema.type === "optional" || valueSchema.type === "nullish") && valueSchema.default !== void 0) {
1070
+ const value$1 = key in input ? input[key] : /* @__PURE__ */ getDefault(valueSchema);
1071
+ const valueDataset = valueSchema["~run"]({ value: value$1 }, config$1);
1072
+ if (valueDataset.issues) {
1073
+ const pathItem = {
1074
+ type: "object",
1075
+ origin: "value",
1076
+ input,
1077
+ key,
1078
+ value: value$1
1079
+ };
1080
+ for (const issue of valueDataset.issues) {
1081
+ if (issue.path) issue.path.unshift(pathItem);
1082
+ else issue.path = [pathItem];
1083
+ dataset.issues?.push(issue);
1084
+ }
1085
+ if (!dataset.issues) dataset.issues = valueDataset.issues;
1086
+ if (config$1.abortEarly) {
1087
+ dataset.typed = false;
1088
+ break;
1089
+ }
1090
+ }
1091
+ if (!valueDataset.typed) dataset.typed = false;
1092
+ dataset.value[key] = valueDataset.value;
1093
+ } else if (valueSchema.fallback !== void 0) dataset.value[key] = /* @__PURE__ */ getFallback(valueSchema);
1094
+ else if (valueSchema.type !== "exact_optional" && valueSchema.type !== "optional" && valueSchema.type !== "nullish") {
1095
+ _addIssue(this, "key", dataset, config$1, {
1096
+ input: void 0,
1097
+ expected: `"${key}"`,
1098
+ path: [{
1099
+ type: "object",
1100
+ origin: "key",
1101
+ input,
1102
+ key,
1103
+ value: input[key]
1104
+ }]
1105
+ });
1106
+ if (config$1.abortEarly) break;
1107
+ }
1108
+ }
1109
+ } else _addIssue(this, "type", dataset, config$1);
1110
+ return dataset;
1111
+ }
1112
+ };
1113
+ }
1114
+ // @__NO_SIDE_EFFECTS__
1115
+ function string(message$1) {
1116
+ return {
1117
+ kind: "schema",
1118
+ type: "string",
1119
+ reference: string,
1120
+ expects: "string",
1121
+ async: false,
1122
+ message: message$1,
1123
+ get "~standard"() {
1124
+ return /* @__PURE__ */ _getStandardProps(this);
1125
+ },
1126
+ "~run"(dataset, config$1) {
1127
+ if (typeof dataset.value === "string") dataset.typed = true;
1128
+ else _addIssue(this, "type", dataset, config$1);
1129
+ return dataset;
1130
+ }
1131
+ };
1132
+ }
1133
+ // @__NO_SIDE_EFFECTS__
1134
+ function pipe(...pipe$1) {
1135
+ return {
1136
+ ...pipe$1[0],
1137
+ pipe: pipe$1,
1138
+ get "~standard"() {
1139
+ return /* @__PURE__ */ _getStandardProps(this);
1140
+ },
1141
+ "~run"(dataset, config$1) {
1142
+ for (const item of pipe$1) if (item.kind !== "metadata") {
1143
+ if (dataset.issues && (item.kind === "schema" || item.kind === "transformation")) {
1144
+ dataset.typed = false;
1145
+ break;
1146
+ }
1147
+ if (!dataset.issues || !config$1.abortEarly && !config$1.abortPipeEarly) dataset = item["~run"](dataset, config$1);
1148
+ }
1149
+ return dataset;
1150
+ }
1151
+ };
1152
+ }
1153
+ // @__NO_SIDE_EFFECTS__
1154
+ function safeParse(schema, input, config$1) {
1155
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
1156
+ return {
1157
+ typed: dataset.typed,
1158
+ success: !dataset.issues,
1159
+ output: dataset.value,
1160
+ issues: dataset.issues
1161
+ };
1162
+ }
1163
+
1164
+ // src/utils/validate-form-fields.ts
1165
+ var validateFormFields = async (fields, options = {}) => {
1166
+ const schema = object({
1167
+ customer_name: pipe(
1168
+ string("required"),
1169
+ nonEmpty("required"),
1170
+ minLength(4, "invalid"),
1171
+ maxLength(40, "invalid")
1172
+ ),
1173
+ customer_email: pipe(string("required"), nonEmpty("required"), email("invalid")),
1174
+ customer_country: pipe(string("required"), nonEmpty("required")),
1175
+ customer_zip_code: options.optionalZipCode ? pipe(string("required")) : pipe(string("required"), nonEmpty("required"))
1176
+ });
1177
+ const result = safeParse(schema, fields);
1178
+ if (result.issues) {
1179
+ const errors = result.issues.reduce((errors2, issue) => {
1180
+ const field = getDotPath(issue);
1181
+ errors2[field] = issue.kind === "schema" ? "required" : issue.message;
1182
+ return errors2;
1183
+ }, {});
1184
+ return { isSuccess: false, errors };
1185
+ }
1186
+ return { isSuccess: true };
1187
+ };
1188
+
1189
+ // src/utils/index.ts
1190
+ var $ = (selector) => {
1191
+ const ele = document.querySelector(selector);
1192
+ if (!ele) {
1193
+ throw new Error(`Cannot find element with selector: ${selector}`);
1194
+ }
1195
+ return ele;
1196
+ };
1197
+ var definePaymentMethod = (paymentMethod) => paymentMethod;
1198
+ var createCheckoutIFrame = (type, baseUrl, params) => {
1199
+ const iframe = document.createElement("iframe");
1200
+ const searchParams = new URLSearchParams(params);
1201
+ const searchParamsStr = searchParams.toString();
1202
+ iframe.src = `${baseUrl}/embeds/v1/${type}${searchParamsStr ? `?${searchParamsStr}` : ""}`;
1203
+ Object.assign(iframe.style, { width: "100%", height: "100%", border: "none" });
1204
+ return iframe;
1205
+ };
1206
+ function collectFraudMetadata() {
1207
+ return {
1208
+ ipAddress: void 0,
1209
+ // IP address should be collected server-side
1210
+ browserInfo: {
1211
+ userAgent: navigator.userAgent,
1212
+ language: navigator.language,
1213
+ screenHeight: window.screen.height,
1214
+ screenWidth: window.screen.width,
1215
+ colorDepth: window.screen.colorDepth,
1216
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
1217
+ },
1218
+ processorFraudInfo: void 0
1219
+ };
1220
+ }
1221
+
1222
+ // src/index.ts
1223
+ var createTunnelXConnection = (baseUrl, apiBaseUrl, token) => {
1224
+ const iframe = createCheckoutIFrame("tunnel-x", baseUrl, {
1225
+ checkout_token: token,
1226
+ api_base_url: apiBaseUrl
1227
+ });
1228
+ document.body.appendChild(iframe);
1229
+ const connection = connectToTunnelXIframe(iframe, {});
1230
+ const unmount = () => {
1231
+ connection.destroy();
1232
+ document.body.removeChild(iframe);
1233
+ };
1234
+ return { unmount, connection };
1235
+ };
1236
+ var PaymentKit = ({ environment, secureToken, paymentMethods }) => {
1237
+ const { baseUrl, apiBaseUrl } = getUrlsForEnvironment(environment);
1238
+ console.log(`[PaymentKit] v${version} initialized (env: ${environment})`);
1239
+ const { connection: tunnelXConnection, unmount: unmountTunnelX } = createTunnelXConnection(
1240
+ baseUrl,
1241
+ apiBaseUrl,
1242
+ secureToken
1243
+ );
1244
+ const paymentKitStates = {
1245
+ baseUrl,
1246
+ apiBaseUrl,
1247
+ secureToken,
1248
+ tunnelXConnection
1249
+ };
1250
+ const pmInstances = paymentMethods.map((paymentMethod) => paymentMethod(paymentKitStates));
1251
+ const externalFuncsMapByPm = pmInstances.reduce(
1252
+ (acc, { name, externalFuncs }) => {
1253
+ acc[name] = externalFuncs;
1254
+ return acc;
1255
+ },
1256
+ {}
1257
+ );
1258
+ const submit = ({
1259
+ paymentMethod: paymentMethodName,
1260
+ fields,
1261
+ options,
1262
+ onSuccess,
1263
+ onError
1264
+ }) => {
1265
+ const paymentMethod = pmInstances.find(({ name }) => name === paymentMethodName);
1266
+ if (!paymentMethod) {
1267
+ onError({ root: "payment_method_not_found" });
1268
+ return;
1269
+ }
1270
+ paymentMethod.internalFuncs.submitPayment(fields, options).then(({ data, errors }) => {
1271
+ errors ? onError(errors) : onSuccess(data);
1272
+ }).catch((e) => {
1273
+ console.error("PaymentKit:submit:catch", e);
1274
+ if (e?.response?.data) {
1275
+ onError(e.response.data);
1276
+ } else if (e?.message) {
1277
+ onError({ root: e.message });
1278
+ } else {
1279
+ onError({ root: "unknown_error" });
1280
+ }
1281
+ });
1282
+ };
1283
+ const cleanup = () => {
1284
+ for (const pm of pmInstances) {
1285
+ if (pm.internalFuncs.cleanup) {
1286
+ pm.internalFuncs.cleanup();
1287
+ }
1288
+ }
1289
+ unmountTunnelX();
1290
+ };
1291
+ return {
1292
+ submit,
1293
+ cleanup,
1294
+ ...externalFuncsMapByPm
1295
+ };
1296
+ };
1297
+ var index_default = PaymentKit;
1298
+
1299
+ // src/penpal/connect-card.ts
1300
+ var connectToCardIframe = (iframe, methods) => {
1301
+ return connectToWindow({ window: iframe.contentWindow, methods });
1302
+ };
1303
+
1304
+ // src/payment-methods/next-action-handlers.ts
1305
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1306
+ var isStripeJsPresent = () => "Stripe" in window;
1307
+ var getLoadedStripe = async (publishableKey) => {
1308
+ for (let i = 0; i < 10; i++) {
1309
+ if (isStripeJsPresent()) {
1310
+ break;
1311
+ }
1312
+ await sleep(500);
1313
+ }
1314
+ if (!isStripeJsPresent()) {
1315
+ throw new Error(
1316
+ 'Stripe.js not loaded. Add this script tag to your HTML <head>:\n<script src="https://js.stripe.com/v3/"><\/script>'
1317
+ );
1318
+ }
1319
+ const stripe = new window.Stripe(publishableKey);
1320
+ return stripe;
1321
+ };
1322
+ var handleStripe3ds = async (nextAction) => {
1323
+ console.log("3DS authentication required, loading Stripe...");
1324
+ const { clientSecret, stripePk } = nextAction;
1325
+ const stripe = await getLoadedStripe(stripePk);
1326
+ const { error: stripeError } = await stripe.confirmCardPayment(clientSecret);
1327
+ if (stripeError) {
1328
+ console.error("3DS authentication failed:", stripeError);
1329
+ return { success: false, error: stripeError.message || "3DS authentication failed" };
1330
+ }
1331
+ console.log("3DS authentication completed");
1332
+ return { success: true };
1333
+ };
1334
+ var handleNextAction = async (nextAction) => {
1335
+ switch (nextAction.type) {
1336
+ case "stripe_3ds":
1337
+ return handleStripe3ds(nextAction);
1338
+ default:
1339
+ return { success: false, error: `Unknown next action type: ${nextAction.type}` };
1340
+ }
1341
+ };
1342
+
1343
+ // src/payment-methods/card.ts
1344
+ var splitName = (fullName) => {
1345
+ const trimmed = fullName.trim();
1346
+ const spaceIndex = trimmed.indexOf(" ");
1347
+ if (spaceIndex === -1) {
1348
+ return { firstName: trimmed };
1349
+ }
1350
+ return {
1351
+ firstName: trimmed.substring(0, spaceIndex),
1352
+ lastName: trimmed.substring(spaceIndex + 1).trim() || void 0
1353
+ };
1354
+ };
1355
+ var mapFieldsToCustomerInfo = (fields) => {
1356
+ const { firstName, lastName } = splitName(fields.customer_name);
1357
+ return {
1358
+ email: fields.customer_email || void 0,
1359
+ firstName,
1360
+ lastName,
1361
+ billingAddress: fields.customer_country || fields.customer_zip_code ? {
1362
+ country: fields.customer_country || void 0,
1363
+ zipCode: fields.customer_zip_code || void 0
1364
+ } : void 0
1365
+ };
1366
+ };
1367
+ var defCreateElement = (states) => {
1368
+ const { baseUrl, apiBaseUrl, cardInputConnections, secureToken } = states;
1369
+ return (type, options) => {
1370
+ const { style, onLoaded, onFocusChange } = options;
1371
+ const mountIFrame = (parentSelector) => {
1372
+ const parent = $(parentSelector);
1373
+ const params = {
1374
+ checkout_token: secureToken,
1375
+ api_base_url: apiBaseUrl
1376
+ };
1377
+ if (style) {
1378
+ params.style = JSON.stringify(style);
1379
+ }
1380
+ const iframe = createCheckoutIFrame(type.replace("_", "-"), baseUrl, params);
1381
+ parent.appendChild(iframe);
1382
+ iframe.onload = () => {
1383
+ const connection = connectToCardIframe(iframe, {
1384
+ onLoaded: onLoaded || (() => {
1385
+ }),
1386
+ onFocusChange: onFocusChange || (() => {
1387
+ })
1388
+ });
1389
+ states.cardInputConnections[type] = connection;
1390
+ };
1391
+ const unmount = () => {
1392
+ const connection = cardInputConnections[type];
1393
+ connection?.destroy();
1394
+ parent.removeChild(iframe);
1395
+ states.cardInputConnections[type] = void 0;
1396
+ };
1397
+ return { unmount };
1398
+ };
1399
+ return { mount: mountIFrame };
1400
+ };
1401
+ };
1402
+ var defSubmitPayment = (states) => {
1403
+ const submitPayment = async (fields) => {
1404
+ const tunnelX = await TunnelXManager.createFromPenpalConnection(states.tunnelXConnection);
1405
+ const validateCardResult = await validateCardFields(states);
1406
+ const validateFormResult = await validateFormFields(fields);
1407
+ if (!(validateCardResult.isSuccess && validateFormResult.isSuccess)) {
1408
+ return { errors: { ...validateCardResult.errors, ...validateFormResult.errors } };
1409
+ }
1410
+ if (!states.cardSetupIntentId) {
1411
+ const res = await tunnelX.publicEndpoints.createCardSetupIntent({
1412
+ checkoutToken: states.secureToken
1413
+ });
1414
+ states.cardSetupIntentId = res.cardSetupIntentId;
1415
+ }
1416
+ const submitCardResult = await submitCardFields(states);
1417
+ if (!submitCardResult.isSuccess) {
1418
+ return { errors: submitCardResult.errors };
1419
+ }
1420
+ const cardSetupIntent = await tunnelX.publicEndpoints.getCardSetupIntent({
1421
+ cardSetupIntentId: states.cardSetupIntentId,
1422
+ checkoutToken: states.secureToken
1423
+ });
1424
+ if (!cardSetupIntent.isCardAllSet) {
1425
+ const errors = {};
1426
+ if (!cardSetupIntent.isCardPanSet) errors.card_pan = "required";
1427
+ if (!cardSetupIntent.isCardExpSet) errors.card_exp = "required";
1428
+ if (!cardSetupIntent.isCardCvcSet) errors.card_cvc = "required";
1429
+ return { errors };
1430
+ }
1431
+ console.log("Card setup intent is set \u2705", cardSetupIntent);
1432
+ console.log("Fields", fields);
1433
+ let currentResult = await tunnelX.publicEndpoints.cardCheckout({
1434
+ checkoutToken: states.secureToken,
1435
+ publicCardCheckoutRequest: {
1436
+ cardSetupIntentId: states.cardSetupIntentId,
1437
+ customerInfo: mapFieldsToCustomerInfo(fields),
1438
+ fraudMetadata: collectFraudMetadata()
1439
+ }
1440
+ });
1441
+ console.log("Card checkout result:", currentResult);
1442
+ const MAX_USER_ACTIONS = 5;
1443
+ let userActionCount = 0;
1444
+ while (currentResult.nextAction && userActionCount < MAX_USER_ACTIONS) {
1445
+ userActionCount++;
1446
+ console.log(`Handling user action ${userActionCount}/${MAX_USER_ACTIONS}...`);
1447
+ const actionResult = await handleNextAction(currentResult.nextAction);
1448
+ console.log("User action completed, verifying checkout...");
1449
+ const verifyResult = await tunnelX.publicEndpoints.cardCheckoutVerify({
1450
+ checkoutToken: states.secureToken
1451
+ });
1452
+ if (verifyResult.nextAction) {
1453
+ if (!actionResult.success) {
1454
+ console.log("3DS failed but cascade triggered new action, continuing loop...");
1455
+ } else {
1456
+ console.log("Another user action required, continuing loop...");
1457
+ }
1458
+ currentResult = verifyResult;
1459
+ continue;
1460
+ }
1461
+ if (!actionResult.success) {
1462
+ console.log("3DS authentication failed, checkout concluded:", verifyResult);
1463
+ return { errors: { root: actionResult.error } };
1464
+ }
1465
+ console.log("Card checkout verified \u2705", verifyResult);
1466
+ return { data: verifyResult };
1467
+ }
1468
+ if (userActionCount >= MAX_USER_ACTIONS) {
1469
+ console.error("Max user actions exceeded");
1470
+ return { errors: { root: "Too many authentication attempts. Please try again." } };
1471
+ }
1472
+ console.log("Card checkout completed \u2705", currentResult);
1473
+ return { data: currentResult };
1474
+ };
1475
+ return submitPayment;
1476
+ };
1477
+ var submitCardFields = async (states) => {
1478
+ const errors = {};
1479
+ const { cardSetupIntentId, cardInputConnections } = states;
1480
+ const submitPromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {
1481
+ const type = _type;
1482
+ const remote = await connection.promise;
1483
+ const result = await remote.onSubmit(cardSetupIntentId || "");
1484
+ if ("error" in result) {
1485
+ errors[type] = result.error;
1486
+ }
1487
+ });
1488
+ await Promise.allSettled(submitPromises);
1489
+ return {
1490
+ errors,
1491
+ isSuccess: Object.keys(errors).length === 0
1492
+ };
1493
+ };
1494
+ var validateCardFields = async (states) => {
1495
+ const errors = {};
1496
+ const { cardInputConnections } = states;
1497
+ const validatePromises = Object.entries(cardInputConnections).map(async ([_type, connection]) => {
1498
+ const type = _type;
1499
+ if (!connection) {
1500
+ errors[type] = "penpal_not_connected";
1501
+ return;
1502
+ }
1503
+ const remote = await connection.promise;
1504
+ const errorMsg = await remote.onValidate();
1505
+ if (errorMsg) {
1506
+ errors[type] = errorMsg;
1507
+ }
1508
+ });
1509
+ await Promise.allSettled(validatePromises);
1510
+ return {
1511
+ errors,
1512
+ isSuccess: Object.keys(errors).length === 0
1513
+ };
1514
+ };
1515
+ var CardPaymentMethod = definePaymentMethod((paymentKitStates) => {
1516
+ const localStates = { ...paymentKitStates, cardInputConnections: {} };
1517
+ return {
1518
+ name: "card",
1519
+ externalFuncs: {
1520
+ createElement: defCreateElement(localStates)
1521
+ },
1522
+ internalFuncs: {
1523
+ submitPayment: defSubmitPayment(localStates),
1524
+ cleanup: () => {
1525
+ for (const connection of Object.values(localStates.cardInputConnections)) {
1526
+ connection?.destroy();
1527
+ }
1528
+ localStates.cardInputConnections = {};
1529
+ }
1530
+ }
1531
+ };
1532
+ });
1533
+ var card_default = CardPaymentMethod;
1534
+
1535
+ // src/payment-methods/stripe-google-pay-adapter.ts
1536
+ var StripeGooglePayAdapter = class {
1537
+ constructor(mockScenario) {
1538
+ __publicField(this, "stripe", null);
1539
+ __publicField(this, "paymentRequest", null);
1540
+ __publicField(this, "mockScenario");
1541
+ this.mockScenario = mockScenario ?? "none" /* None */;
1542
+ }
1543
+ initialize(publishableKey) {
1544
+ switch (this.mockScenario) {
1545
+ case "none" /* None */: {
1546
+ console.log("[GooglePay] initialize called");
1547
+ if (!window.Stripe) {
1548
+ console.log("[GooglePay] Stripe.js not loaded");
1549
+ return false;
1550
+ }
1551
+ this.stripe = window.Stripe(publishableKey);
1552
+ const success = this.stripe !== null;
1553
+ console.log("[GooglePay] initialize result:", success);
1554
+ return success;
1555
+ }
1556
+ case "success" /* Success */:
1557
+ case "cancelled" /* Cancelled */: {
1558
+ console.log("[MockGooglePay] initialize called");
1559
+ console.log("[MockGooglePay] initialize result:", true);
1560
+ return true;
1561
+ }
1562
+ }
1563
+ }
1564
+ createPaymentRequest(config) {
1565
+ switch (this.mockScenario) {
1566
+ case "none" /* None */: {
1567
+ console.log("[GooglePay] createPaymentRequest called", config);
1568
+ if (!this.stripe) {
1569
+ throw new Error("Stripe not initialized");
1570
+ }
1571
+ this.paymentRequest = this.stripe.paymentRequest(config);
1572
+ console.log("[GooglePay] paymentRequest created");
1573
+ return;
1574
+ }
1575
+ case "success" /* Success */:
1576
+ case "cancelled" /* Cancelled */: {
1577
+ console.log("[MockGooglePay] createPaymentRequest called", config);
1578
+ return;
1579
+ }
1580
+ }
1581
+ }
1582
+ async canMakePayment() {
1583
+ switch (this.mockScenario) {
1584
+ case "none" /* None */: {
1585
+ console.log("[GooglePay] canMakePayment called");
1586
+ if (!this.paymentRequest) {
1587
+ console.log("[GooglePay] canMakePayment: no paymentRequest");
1588
+ return false;
1589
+ }
1590
+ const result = await this.paymentRequest.canMakePayment();
1591
+ const isAvailable = result?.googlePay ?? false;
1592
+ console.log("[GooglePay] canMakePayment result:", { result, isAvailable });
1593
+ return isAvailable;
1594
+ }
1595
+ case "success" /* Success */:
1596
+ case "cancelled" /* Cancelled */: {
1597
+ console.log("[MockGooglePay] canMakePayment called");
1598
+ console.log("[MockGooglePay] canMakePayment result:", { result: { googlePay: true }, isAvailable: true });
1599
+ return true;
1600
+ }
1601
+ }
1602
+ }
1603
+ async showPaymentSheet() {
1604
+ switch (this.mockScenario) {
1605
+ case "none" /* None */: {
1606
+ console.log("[GooglePay] showPaymentSheet called");
1607
+ if (!this.paymentRequest) {
1608
+ console.log("[GooglePay] showPaymentSheet: no paymentRequest");
1609
+ return { success: false, error: "Payment request not created" };
1610
+ }
1611
+ return new Promise((resolve) => {
1612
+ this.paymentRequest.on("paymentmethod", (event) => {
1613
+ console.log("[GooglePay] showPaymentSheet: paymentmethod event", {
1614
+ paymentMethodId: event.paymentMethod.id
1615
+ });
1616
+ resolve({
1617
+ success: true,
1618
+ paymentMethodId: event.paymentMethod.id,
1619
+ complete: (status) => {
1620
+ console.log("[GooglePay] complete called:", status);
1621
+ event.complete(status);
1622
+ }
1623
+ });
1624
+ });
1625
+ this.paymentRequest.on("cancel", () => {
1626
+ console.log("[GooglePay] showPaymentSheet: cancelled by user");
1627
+ resolve({ success: false, cancelled: true });
1628
+ });
1629
+ console.log("[GooglePay] showing payment sheet...");
1630
+ this.paymentRequest.show();
1631
+ });
1632
+ }
1633
+ case "success" /* Success */: {
1634
+ console.log("[MockGooglePay] showPaymentSheet: success");
1635
+ await new Promise((resolve) => setTimeout(resolve, 500));
1636
+ return {
1637
+ success: true,
1638
+ paymentMethodId: "pm_mock_123456789",
1639
+ complete: (status) => console.log(`[MockGooglePay] complete: ${status}`)
1640
+ };
1641
+ }
1642
+ case "cancelled" /* Cancelled */: {
1643
+ console.log("[MockGooglePay] showPaymentSheet: cancelled");
1644
+ await new Promise((resolve) => setTimeout(resolve, 500));
1645
+ return { success: false, cancelled: true };
1646
+ }
1647
+ }
1648
+ }
1649
+ async confirmCardSetup(clientSecret, paymentMethodId) {
1650
+ switch (this.mockScenario) {
1651
+ case "none" /* None */: {
1652
+ console.log("[GooglePay] confirmCardSetup called", {
1653
+ clientSecret: `${clientSecret.slice(0, 20)}...`,
1654
+ paymentMethodId
1655
+ });
1656
+ if (!this.stripe) {
1657
+ console.log("[GooglePay] confirmCardSetup: Stripe not initialized");
1658
+ return { success: false, error: "Stripe not initialized" };
1659
+ }
1660
+ const { error } = await this.stripe.confirmCardSetup(clientSecret, {
1661
+ payment_method: paymentMethodId
1662
+ });
1663
+ if (error) {
1664
+ console.log("[GooglePay] confirmCardSetup error:", error);
1665
+ return { success: false, error: error.message ?? "Card setup failed" };
1666
+ }
1667
+ console.log("[GooglePay] confirmCardSetup success");
1668
+ return { success: true };
1669
+ }
1670
+ case "success" /* Success */: {
1671
+ console.log("[MockGooglePay] confirmCardSetup called", { clientSecret, paymentMethodId });
1672
+ return { success: true };
1673
+ }
1674
+ case "cancelled" /* Cancelled */: {
1675
+ throw new Error("confirmCardSetup should not be called when scenario is Cancelled");
1676
+ }
1677
+ }
1678
+ }
1679
+ };
1680
+
1681
+ // src/payment-methods/google-pay.ts
1682
+ async function apiCall(url, options) {
1683
+ const response = await fetch(url, options);
1684
+ if (!response.ok) {
1685
+ let errorMessage = `Request failed (${response.status})`;
1686
+ try {
1687
+ const errorData = await response.json();
1688
+ errorMessage = errorData.detail || errorMessage;
1689
+ } catch {
1690
+ errorMessage = response.statusText || errorMessage;
1691
+ }
1692
+ return { error: errorMessage };
1693
+ }
1694
+ return { data: await response.json() };
1695
+ }
1696
+ function validateOptions(options) {
1697
+ if (!options?.processorId) {
1698
+ return { processor_id: "Processor ID is required" };
1699
+ }
1700
+ if (!options?.customerInfo?.first_name || !options?.customerInfo?.last_name) {
1701
+ return { customer_name: "Customer first and last name are required" };
1702
+ }
1703
+ return null;
1704
+ }
1705
+ function getMockScenarioStr(mockScenario) {
1706
+ return mockScenario && mockScenario !== "none" /* None */ ? mockScenario : void 0;
1707
+ }
1708
+ async function callStartEndpoint(apiBaseUrl, secureToken, options, mockScenarioStr) {
1709
+ return apiCall(`${apiBaseUrl}/api/checkout/${secureToken}/google-pay/start`, {
1710
+ method: "POST",
1711
+ headers: { "Content-Type": "application/json" },
1712
+ body: JSON.stringify({
1713
+ processor_id: options.processorId,
1714
+ customer_info: options.customerInfo,
1715
+ fraud_metadata: collectFraudMetadata(),
1716
+ mock_scenario: mockScenarioStr
1717
+ })
1718
+ });
1719
+ }
1720
+ function initializeAdapter(stripePk, startData, mockScenario) {
1721
+ const adapter = new StripeGooglePayAdapter(mockScenario);
1722
+ if (!adapter.initialize(stripePk)) {
1723
+ return { error: 'Stripe.js not loaded. Add <script src="https://js.stripe.com/v3/"><\/script> to your page.' };
1724
+ }
1725
+ const prConfig = {
1726
+ country: startData.country,
1727
+ currency: startData.currency.toLowerCase(),
1728
+ total: { label: "Total", amount: startData.amount },
1729
+ requestPayerName: true,
1730
+ requestPayerEmail: true
1731
+ };
1732
+ console.log("[GooglePay] PaymentRequest config:", prConfig);
1733
+ adapter.createPaymentRequest(prConfig);
1734
+ return { adapter };
1735
+ }
1736
+ async function showPaymentSheetAndConfirm(adapter, clientSecret) {
1737
+ const isAvailable = await adapter.canMakePayment();
1738
+ console.log("[GooglePay] canMakePayment result:", isAvailable);
1739
+ if (!isAvailable) {
1740
+ return { success: false, error: "Google Pay not available on this device" };
1741
+ }
1742
+ const paymentResult = await adapter.showPaymentSheet();
1743
+ if (!paymentResult.success) {
1744
+ if ("cancelled" in paymentResult && paymentResult.cancelled) {
1745
+ return { success: false, error: "Google Pay cancelled by user" };
1746
+ }
1747
+ const errorMessage = "error" in paymentResult ? paymentResult.error : "Unknown error";
1748
+ return { success: false, error: errorMessage };
1749
+ }
1750
+ const confirmResult = await adapter.confirmCardSetup(clientSecret, paymentResult.paymentMethodId);
1751
+ if (!confirmResult.success) {
1752
+ paymentResult.complete("fail");
1753
+ return { success: false, error: confirmResult.error };
1754
+ }
1755
+ paymentResult.complete("success");
1756
+ return { success: true };
1757
+ }
1758
+ async function callConfirmEndpoint(apiBaseUrl, secureToken, mockScenarioStr) {
1759
+ const result = await apiCall(
1760
+ `${apiBaseUrl}/api/checkout/${secureToken}/google-pay/confirm`,
1761
+ {
1762
+ method: "POST",
1763
+ headers: { "Content-Type": "application/json" },
1764
+ body: JSON.stringify({ mock_scenario: mockScenarioStr })
1765
+ }
1766
+ );
1767
+ if (result.error || !result.data) {
1768
+ return { errors: { google_pay: result.error || "Failed to confirm payment" } };
1769
+ }
1770
+ const confirmData = result.data;
1771
+ if (confirmData.charge_status === "success") {
1772
+ return {
1773
+ data: {
1774
+ id: confirmData.transaction_id,
1775
+ checkoutAttemptId: confirmData.checkout_attempt_id,
1776
+ checkoutSessionId: secureToken,
1777
+ state: "checkout_succeeded"
1778
+ }
1779
+ };
1780
+ }
1781
+ return { errors: { google_pay: confirmData.error_message || "Payment failed" } };
1782
+ }
1783
+ var defSubmitPayment2 = (states) => {
1784
+ const submitPayment = async (_fields, options) => {
1785
+ const { apiBaseUrl, secureToken } = states;
1786
+ const gpayOptions = options;
1787
+ const validationError = validateOptions(gpayOptions);
1788
+ if (validationError) {
1789
+ return { errors: validationError };
1790
+ }
1791
+ try {
1792
+ const mockScenarioStr = getMockScenarioStr(gpayOptions.mockScenario);
1793
+ const startResult = await callStartEndpoint(apiBaseUrl, secureToken, gpayOptions, mockScenarioStr);
1794
+ if (startResult.error || !startResult.data) {
1795
+ return { errors: { google_pay: startResult.error || "Failed to start Google Pay" } };
1796
+ }
1797
+ const { adapter, error: adapterError } = initializeAdapter(
1798
+ startResult.data.stripe_pk,
1799
+ startResult.data,
1800
+ gpayOptions.mockScenario
1801
+ );
1802
+ if (!adapter) {
1803
+ return { errors: { google_pay: adapterError } };
1804
+ }
1805
+ const paymentResult = await showPaymentSheetAndConfirm(adapter, startResult.data.client_secret);
1806
+ if (!paymentResult.success) {
1807
+ return { errors: { google_pay: paymentResult.error } };
1808
+ }
1809
+ return await callConfirmEndpoint(apiBaseUrl, secureToken, mockScenarioStr);
1810
+ } catch (error) {
1811
+ return { errors: { google_pay: `Google Pay error: ${error}` } };
1812
+ }
1813
+ };
1814
+ return submitPayment;
1815
+ };
1816
+ var GooglePayPaymentMethod = definePaymentMethod((paymentKitStates) => {
1817
+ return {
1818
+ name: "google_pay",
1819
+ externalFuncs: {},
1820
+ internalFuncs: {
1821
+ submitPayment: defSubmitPayment2(paymentKitStates),
1822
+ cleanup: () => {
1823
+ }
1824
+ }
1825
+ };
1826
+ });
1827
+ var google_pay_default = GooglePayPaymentMethod;
1828
+
1829
+ // src/payment-methods/paypal.ts
1830
+ var defSubmitPayment3 = (states) => {
1831
+ const submitPayment = async (_fields, options) => {
1832
+ const { apiBaseUrl, secureToken } = states;
1833
+ const paypalOptions = options;
1834
+ if (!paypalOptions?.processorId) {
1835
+ return { errors: { processor_id: "Processor ID is required" } };
1836
+ }
1837
+ if (!paypalOptions?.customerInfo?.first_name || !paypalOptions?.customerInfo?.last_name) {
1838
+ return {
1839
+ errors: {
1840
+ customer_name: "Customer first and last name are required"
1841
+ }
1842
+ };
1843
+ }
1844
+ try {
1845
+ const startResponse = await fetch(`${apiBaseUrl}/api/checkout/${secureToken}/paypal/start`, {
1846
+ method: "POST",
1847
+ headers: {
1848
+ "Content-Type": "application/json"
1849
+ },
1850
+ body: JSON.stringify({
1851
+ processor_id: paypalOptions.processorId,
1852
+ customer_info: paypalOptions.customerInfo,
1853
+ fraud_metadata: collectFraudMetadata()
1854
+ })
1855
+ });
1856
+ if (!startResponse.ok) {
1857
+ let errorMessage = `Failed to start PayPal checkout (${startResponse.status})`;
1858
+ try {
1859
+ const errorData = await startResponse.json();
1860
+ errorMessage = errorData.detail || errorMessage;
1861
+ } catch {
1862
+ errorMessage = startResponse.statusText || errorMessage;
1863
+ }
1864
+ return {
1865
+ errors: {
1866
+ paypal: errorMessage
1867
+ }
1868
+ };
1869
+ }
1870
+ const { approval_url, popup_token, checkout_attempt_id } = await startResponse.json();
1871
+ const popup = window.open(approval_url, "PayPal", "width=600,height=700");
1872
+ if (!popup) {
1873
+ return {
1874
+ errors: {
1875
+ paypal: "Failed to open PayPal popup. Please allow popups for this site."
1876
+ }
1877
+ };
1878
+ }
1879
+ states.popup = popup;
1880
+ return new Promise((resolve) => {
1881
+ const pollInterval = setInterval(async () => {
1882
+ try {
1883
+ const statusResponse = await fetch(
1884
+ `${apiBaseUrl}/api/checkout/${secureToken}/popup/status?popup_token=${popup_token}`
1885
+ );
1886
+ if (!statusResponse.ok) {
1887
+ clearInterval(pollInterval);
1888
+ popup.close();
1889
+ resolve({
1890
+ errors: {
1891
+ paypal: "Failed to check PayPal status"
1892
+ }
1893
+ });
1894
+ return;
1895
+ }
1896
+ const statusResult = await statusResponse.json();
1897
+ if (statusResult.completed) {
1898
+ clearInterval(pollInterval);
1899
+ popup.close();
1900
+ if (statusResult.status === "success") {
1901
+ const result = statusResult.result || {
1902
+ id: checkout_attempt_id,
1903
+ checkoutAttemptId: checkout_attempt_id,
1904
+ checkoutSessionId: secureToken,
1905
+ state: "checkout_succeeded"
1906
+ };
1907
+ resolve({ data: result });
1908
+ } else {
1909
+ resolve({
1910
+ errors: {
1911
+ paypal: statusResult.error || `PayPal checkout ${statusResult.status}`
1912
+ }
1913
+ });
1914
+ }
1915
+ return;
1916
+ }
1917
+ if (popup.closed) {
1918
+ clearInterval(pollInterval);
1919
+ resolve({
1920
+ errors: {
1921
+ paypal: "PayPal popup closed by user"
1922
+ }
1923
+ });
1924
+ return;
1925
+ }
1926
+ } catch (error) {
1927
+ clearInterval(pollInterval);
1928
+ popup.close();
1929
+ resolve({
1930
+ errors: {
1931
+ paypal: `Polling error: ${error}`
1932
+ }
1933
+ });
1934
+ }
1935
+ }, 2e3);
1936
+ states.pollInterval = pollInterval;
1937
+ });
1938
+ } catch (error) {
1939
+ return {
1940
+ errors: {
1941
+ paypal: `PayPal checkout error: ${error}`
1942
+ }
1943
+ };
1944
+ }
1945
+ };
1946
+ return submitPayment;
1947
+ };
1948
+ var PayPalPaymentMethod = definePaymentMethod((paymentKitStates) => {
1949
+ const localStates = { ...paymentKitStates };
1950
+ return {
1951
+ name: "paypal",
1952
+ externalFuncs: {
1953
+ // PayPal doesn't need createElement like card iframes
1954
+ },
1955
+ internalFuncs: {
1956
+ submitPayment: defSubmitPayment3(localStates),
1957
+ cleanup: () => {
1958
+ if (localStates.pollInterval) {
1959
+ clearInterval(localStates.pollInterval);
1960
+ }
1961
+ if (localStates.popup && !localStates.popup.closed) {
1962
+ localStates.popup.close();
1963
+ }
1964
+ }
1965
+ }
1966
+ };
1967
+ });
1968
+ var paypal_default = PayPalPaymentMethod;
1969
+
1970
+ // src/cdn.ts
1971
+ var cdn_default = index_default;
1972
+ var PaymentMethods = {
1973
+ card: card_default,
1974
+ googlePay: google_pay_default,
1975
+ paypal: paypal_default
1976
+ };
1977
+ return __toCommonJS(cdn_exports);
1978
+ })();
1979
+ //# sourceMappingURL=paymentkit.js.map