@benbraide/inlinejs-stripe 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 benbraide
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # inlinejs-stripe
2
+ Stripe plugin for the InlineJS reactive framework
@@ -0,0 +1,24 @@
1
+ /// <reference types="stripe-v3" />
2
+ export interface IStripeStyle {
3
+ base?: stripe.elements.Style;
4
+ complete?: stripe.elements.Style;
5
+ empty?: stripe.elements.Style;
6
+ invalid?: stripe.elements.Style;
7
+ paymentRequestButton?: stripe.elements.PaymentRequestButtonStyleOptions;
8
+ }
9
+ export interface IStripeClass {
10
+ base?: string;
11
+ complete?: string;
12
+ empty?: string;
13
+ focus?: string;
14
+ invalid?: string;
15
+ webkitAutofill?: string;
16
+ }
17
+ export interface IStripeBillingDetails {
18
+ name: string;
19
+ email?: string;
20
+ phone?: string;
21
+ address?: string;
22
+ }
23
+ export declare const StripeDirectiveHandler: import("@benbraide/inlinejs").IDirectiveHandlerCallbackDetails;
24
+ export declare function StripeDirectiveHandlerCompact(): void;
@@ -0,0 +1,436 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StripeDirectiveHandlerCompact = exports.StripeDirectiveHandler = void 0;
4
+ const inlinejs_1 = require("@benbraide/inlinejs");
5
+ const StripeSpecialKeys = ['submit', 'save', 'name', 'email', 'phone', 'address'];
6
+ const StripeKeys = {
7
+ 'number': 'cardNumber',
8
+ 'expiry': 'cardExpiry',
9
+ 'cvc': 'cardCvc',
10
+ 'postal': 'postalCode',
11
+ 'zip': 'postalCode',
12
+ };
13
+ const StripeDirectiveName = 'stripe';
14
+ let StripePublicKey = '';
15
+ let StripeUrl = 'https://js.stripe.com/v3/';
16
+ let StripeStyles = null;
17
+ let StripeClasses = null;
18
+ exports.StripeDirectiveHandler = (0, inlinejs_1.CreateDirectiveHandlerCallback)(StripeDirectiveName, ({ componentId, component, contextElement, expression, argKey, argOptions }) => {
19
+ if ((0, inlinejs_1.BindEvent)({ contextElement, expression,
20
+ component: (component || componentId),
21
+ key: StripeDirectiveName,
22
+ event: argKey,
23
+ defaultEvent: 'success',
24
+ eventWhitelist: ['error', 'before', 'after', 'ready', 'focus', 'complete'],
25
+ options: argOptions,
26
+ optionBlacklist: ['window', 'document', 'outside'],
27
+ })) {
28
+ return;
29
+ }
30
+ let resolvedComponent = (component || (0, inlinejs_1.FindComponentById)(componentId)), elementScope = resolvedComponent === null || resolvedComponent === void 0 ? void 0 : resolvedComponent.FindElementScope(contextElement);
31
+ if (!resolvedComponent || !elementScope) {
32
+ return (0, inlinejs_1.JournalError)('Failed to retrieve element scope.', `InlineJS.${StripeDirectiveName}`, contextElement);
33
+ }
34
+ let localKey = `\$${StripeDirectiveName}`, detailsKey = `#${StripeDirectiveName}`;
35
+ if (localKey in (elementScope.GetLocals() || {})) { //Already initialized
36
+ return;
37
+ }
38
+ if (StripeSpecialKeys.includes(argKey)) { //No Stripe binding
39
+ let details = resolvedComponent.FindElementLocalValue(contextElement, detailsKey, true);
40
+ if (details) {
41
+ details.specialMounts[argKey] = contextElement;
42
+ elementScope.SetLocal(localKey, (0, inlinejs_1.CreateInplaceProxy)((0, inlinejs_1.BuildGetterProxyOptions)({
43
+ getter: (prop) => {
44
+ var _a;
45
+ if (prop === 'parent') {
46
+ return (contextElement.parentElement ? (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.FindElementLocalValue(contextElement.parentElement, localKey, true) : null);
47
+ }
48
+ },
49
+ lookup: ['parent'],
50
+ })));
51
+ }
52
+ return;
53
+ }
54
+ if (argKey in StripeKeys) { //Bind Stripe field
55
+ let details = resolvedComponent.FindElementLocalValue(contextElement, detailsKey, true);
56
+ if (!details) { //No parent
57
+ return;
58
+ }
59
+ let id = resolvedComponent.GenerateUniqueId(`${StripeDirectiveName}_proxy_`), field = {
60
+ name: argKey,
61
+ mount: contextElement,
62
+ element: details.elements.create(StripeKeys[argKey], {
63
+ style: (StripeStyles || undefined),
64
+ classes: (StripeClasses || undefined),
65
+ }),
66
+ ready: false,
67
+ complete: false,
68
+ error: undefined,
69
+ };
70
+ if (!field.element) {
71
+ return;
72
+ }
73
+ field.element.mount(contextElement);
74
+ details.fields.push(field);
75
+ elementScope.SetLocal(localKey, (0, inlinejs_1.CreateInplaceProxy)((0, inlinejs_1.BuildGetterProxyOptions)({
76
+ getter: (prop) => {
77
+ var _a, _b, _c, _d;
78
+ if (prop === 'complete') {
79
+ (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
80
+ return field.complete;
81
+ }
82
+ if (prop === 'focused') {
83
+ (_b = (0, inlinejs_1.FindComponentById)(componentId)) === null || _b === void 0 ? void 0 : _b.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
84
+ return field.focused;
85
+ }
86
+ if (prop === 'error') {
87
+ (_c = (0, inlinejs_1.FindComponentById)(componentId)) === null || _c === void 0 ? void 0 : _c.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
88
+ return field.error;
89
+ }
90
+ if (prop === 'parent') {
91
+ return (contextElement.parentElement ? (_d = (0, inlinejs_1.FindComponentById)(componentId)) === null || _d === void 0 ? void 0 : _d.FindElementLocalValue(contextElement.parentElement, localKey, true) : null);
92
+ }
93
+ if (prop === 'clear') {
94
+ return () => {
95
+ if (field.element) {
96
+ field.element.clear();
97
+ }
98
+ };
99
+ }
100
+ if (prop === 'focus') {
101
+ return () => {
102
+ if (field.element) {
103
+ field.element.focus();
104
+ }
105
+ };
106
+ }
107
+ if (prop === 'blur') {
108
+ return () => {
109
+ if (field.element) {
110
+ field.element.blur();
111
+ }
112
+ };
113
+ }
114
+ },
115
+ lookup: ['complete', 'focused', 'error', 'parent', 'clear', 'focus', 'blur'],
116
+ })));
117
+ let fields = details.fields;
118
+ elementScope.AddUninitCallback(() => {
119
+ var _a;
120
+ (_a = field.element) === null || _a === void 0 ? void 0 : _a.destroy();
121
+ fields.splice(fields.indexOf(field), 1);
122
+ });
123
+ field.element.on('ready', () => {
124
+ if (!field.ready) {
125
+ field.ready = true;
126
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.ready`));
127
+ details.onReady();
128
+ }
129
+ });
130
+ field.element.on('change', (e) => {
131
+ var _a, _b;
132
+ if ((e === null || e === void 0 ? void 0 : e.complete) === field.complete) {
133
+ return;
134
+ }
135
+ field.complete = e === null || e === void 0 ? void 0 : e.complete;
136
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.complete`, {
137
+ detail: {
138
+ completed: e === null || e === void 0 ? void 0 : e.complete,
139
+ },
140
+ }));
141
+ if (field.complete) {
142
+ if (field.error) {
143
+ field.error = undefined;
144
+ (0, inlinejs_1.AddChanges)('set', `${id}.error`, 'error', (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
145
+ }
146
+ if (details.options.autofocus) { //Focus next if any
147
+ let index = details.fields.indexOf(field);
148
+ if (index != -1 && index < (details.fields.length - 1)) {
149
+ let nextField = details.fields[index + 1];
150
+ if (nextField.element) {
151
+ nextField.element.focus();
152
+ }
153
+ else if ('focus' in nextField.mount && typeof nextField.mount.focus === 'function') {
154
+ nextField.mount.focus();
155
+ }
156
+ }
157
+ else if (details.specialMounts.submit) {
158
+ details.specialMounts.submit.focus();
159
+ }
160
+ }
161
+ }
162
+ else if ((e === null || e === void 0 ? void 0 : e.error) && e.error.message !== field.error) {
163
+ field.error = e.error.message;
164
+ (0, inlinejs_1.AddChanges)('set', `${id}.error`, 'error', (_b = (0, inlinejs_1.FindComponentById)(componentId)) === null || _b === void 0 ? void 0 : _b.GetBackend().changes);
165
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.error`, {
166
+ detail: {
167
+ message: e.error.message,
168
+ },
169
+ }));
170
+ }
171
+ details.onChange();
172
+ });
173
+ field.element.on('focus', () => {
174
+ var _a;
175
+ if (!field.focused) {
176
+ field.focused = true;
177
+ (0, inlinejs_1.AddChanges)('set', `${id}.focused`, 'focused', (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
178
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.focus`, {
179
+ detail: {
180
+ focused: true,
181
+ },
182
+ }));
183
+ }
184
+ });
185
+ field.element.on('blur', () => {
186
+ var _a;
187
+ if (field.focused) {
188
+ field.focused = false;
189
+ (0, inlinejs_1.AddChanges)('set', `${id}.focused`, 'focused', (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
190
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.focus`, {
191
+ detail: {
192
+ focused: false,
193
+ },
194
+ }));
195
+ }
196
+ });
197
+ return;
198
+ }
199
+ let stripeInstance = null, elements = null, backlog = new Array(), init = () => {
200
+ (0, inlinejs_1.EvaluateLater)({ componentId, contextElement, expression })((value) => {
201
+ stripeInstance = Stripe(value || StripePublicKey);
202
+ if (!stripeInstance) {
203
+ return (0, inlinejs_1.JournalError)('Failed to initialize stripe', `InlineJS.${StripeDirectiveName}.Init`, contextElement);
204
+ }
205
+ elements = stripeInstance.elements();
206
+ if (!elements) {
207
+ return (0, inlinejs_1.JournalError)('Failed to initialize stripe', `InlineJS.${StripeDirectiveName}.Init`, contextElement);
208
+ }
209
+ backlog.splice(0).forEach(callback => (0, inlinejs_1.JournalTry)(callback, `InlineJS.${StripeDirectiveName}.Init`, contextElement));
210
+ });
211
+ };
212
+ let fields = new Array(), specialMounts = {
213
+ submit: null,
214
+ save: null,
215
+ name: null,
216
+ email: null,
217
+ phone: null,
218
+ address: null,
219
+ };
220
+ let id = resolvedComponent.GenerateUniqueId(`${StripeDirectiveName}_proxy_`), options = (0, inlinejs_1.ResolveOptions)({
221
+ options: {
222
+ autofocus: false,
223
+ nexttick: false,
224
+ manual: false,
225
+ },
226
+ list: argOptions,
227
+ });
228
+ let active = false, complete = false, errorCount = 0, setComplete = (value) => {
229
+ var _a;
230
+ if (value != complete) {
231
+ complete = value;
232
+ (0, inlinejs_1.AddChanges)('set', `${id}.complete`, 'complete', (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
233
+ }
234
+ };
235
+ let details = { fields, specialMounts, options,
236
+ onReady: () => { var _a; return (0, inlinejs_1.AddChanges)('set', `${id}.readyCount`, 'readyCount', (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes); },
237
+ onChange: () => {
238
+ var _a;
239
+ setComplete(!fields.find(field => !field.complete));
240
+ let currentErrorCount = fields.reduce((prev, field) => (prev + (field.error ? 1 : 0)), 0);
241
+ if (currentErrorCount != errorCount) { //Error list changed
242
+ errorCount = currentErrorCount;
243
+ (0, inlinejs_1.AddChanges)('set', `${id}.errors`, 'errors', (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
244
+ }
245
+ },
246
+ };
247
+ let setActive = (value) => {
248
+ var _a;
249
+ if (value != active) {
250
+ active = value;
251
+ (0, inlinejs_1.AddChanges)('set', `${id}.active`, 'active', (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
252
+ }
253
+ };
254
+ let evaluate = (0, inlinejs_1.EvaluateLater)({ componentId, contextElement, expression }), onSuccess = (response) => {
255
+ var _a;
256
+ if (!response.error) {
257
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.success`, {
258
+ detail: {
259
+ intent: response.paymentIntent,
260
+ },
261
+ }));
262
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.after`));
263
+ setActive(false);
264
+ if (options.nexttick) {
265
+ (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes.AddNextTickHandler(() => evaluate());
266
+ }
267
+ else { //Immediate evaluation
268
+ evaluate();
269
+ }
270
+ }
271
+ else { //Error
272
+ onError(response.error.message || '');
273
+ }
274
+ };
275
+ let onError = (err) => {
276
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.error`, {
277
+ detail: {
278
+ type: 'host',
279
+ message: err,
280
+ },
281
+ }));
282
+ reportError('host', err);
283
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.after`));
284
+ setActive(false);
285
+ };
286
+ let reportError = (type, message) => {
287
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.error`, {
288
+ detail: { type, message },
289
+ }));
290
+ };
291
+ let getPaymentDetails = (paymentMethod, save) => {
292
+ var _a;
293
+ if (paymentMethod && typeof paymentMethod === 'string') {
294
+ return {
295
+ payment_method: paymentMethod,
296
+ setup_future_usage: (save ? 'off_session' : undefined),
297
+ };
298
+ }
299
+ let cardElement = (_a = fields.find(field => (field.name === 'number'))) === null || _a === void 0 ? void 0 : _a.element;
300
+ if (!cardElement) {
301
+ return null;
302
+ }
303
+ let billingDetails = {}, getBillingDetail = (key) => {
304
+ if (paymentMethod) {
305
+ return (paymentMethod[key] || (specialMounts[key] ? specialMounts[key].value : undefined));
306
+ }
307
+ return (specialMounts[key] ? specialMounts[key].value : undefined);
308
+ };
309
+ ['name', 'email', 'phone', 'address'].forEach((key) => {
310
+ if (key === 'address') {
311
+ billingDetails.address = {
312
+ line1: getBillingDetail(key),
313
+ };
314
+ }
315
+ else {
316
+ billingDetails[key] = getBillingDetail(key);
317
+ }
318
+ });
319
+ if (!save && specialMounts.save && specialMounts.save instanceof HTMLInputElement) {
320
+ save = specialMounts.save.checked;
321
+ }
322
+ return {
323
+ payment_method: {
324
+ card: cardElement,
325
+ billing_details: billingDetails,
326
+ },
327
+ setup_future_usage: (save ? 'off_session' : undefined),
328
+ };
329
+ };
330
+ let payOrSetup = (callback, hasPaymentMethod = false) => {
331
+ if (hasPaymentMethod || (complete && !fields.find(field => !!field.error))) {
332
+ setActive(true);
333
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.before`));
334
+ callback();
335
+ }
336
+ else { //Error
337
+ reportError('incomplete', 'Please fill in all required fields.');
338
+ }
339
+ };
340
+ elementScope.SetLocal(detailsKey, details);
341
+ elementScope.SetLocal(localKey, (0, inlinejs_1.CreateInplaceProxy)((0, inlinejs_1.BuildProxyOptions)({
342
+ getter: (prop) => {
343
+ var _a, _b, _c, _d;
344
+ if (prop === 'bind') {
345
+ return () => {
346
+ if (!stripeInstance) {
347
+ bind();
348
+ }
349
+ };
350
+ }
351
+ if (prop === 'active') {
352
+ (_a = (0, inlinejs_1.FindComponentById)(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
353
+ return active;
354
+ }
355
+ if (prop === 'readyCount') {
356
+ (_b = (0, inlinejs_1.FindComponentById)(componentId)) === null || _b === void 0 ? void 0 : _b.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
357
+ return fields.reduce((prev, field) => (prev + (field.ready ? 1 : 0)), 0);
358
+ }
359
+ if (prop === 'complete') {
360
+ (_c = (0, inlinejs_1.FindComponentById)(componentId)) === null || _c === void 0 ? void 0 : _c.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
361
+ return complete;
362
+ }
363
+ if (prop === 'errors') {
364
+ (_d = (0, inlinejs_1.FindComponentById)(componentId)) === null || _d === void 0 ? void 0 : _d.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
365
+ return fields.filter(field => !!field.error).map(field => field.error);
366
+ }
367
+ if (prop === 'instance') {
368
+ return stripeInstance;
369
+ }
370
+ if (prop === 'publicKey') {
371
+ return StripePublicKey;
372
+ }
373
+ if (prop === 'styles') {
374
+ return StripeStyles;
375
+ }
376
+ if (prop === 'classes') {
377
+ return StripeClasses;
378
+ }
379
+ if (prop === 'url') {
380
+ return StripeUrl;
381
+ }
382
+ if (prop === 'pay') {
383
+ return (clientSecret, paymentMethod, save = false) => {
384
+ payOrSetup(() => {
385
+ let paymentDetails = getPaymentDetails(paymentMethod, save);
386
+ if (paymentDetails) {
387
+ stripeInstance === null || stripeInstance === void 0 ? void 0 : stripeInstance.confirmCardPayment(clientSecret, paymentDetails).then(onSuccess).catch(onError);
388
+ }
389
+ }, (!!paymentMethod && typeof paymentMethod === 'string'));
390
+ };
391
+ }
392
+ if (prop === 'setup') {
393
+ return (clientSecret, paymentMethod, save = false) => {
394
+ payOrSetup(() => {
395
+ let paymentDetails = getPaymentDetails(paymentMethod, save);
396
+ if (paymentDetails) {
397
+ stripeInstance === null || stripeInstance === void 0 ? void 0 : stripeInstance.confirmCardSetup(clientSecret, paymentDetails).then(onSuccess).catch(onError);
398
+ }
399
+ }, (!!paymentMethod && typeof paymentMethod === 'string'));
400
+ };
401
+ }
402
+ },
403
+ setter: (prop, value) => {
404
+ if (prop === 'publicKey') {
405
+ StripePublicKey = value;
406
+ }
407
+ else if (prop === 'styles') {
408
+ StripeStyles = value;
409
+ }
410
+ else if (prop === 'classes') {
411
+ StripeClasses = value;
412
+ }
413
+ else if (prop === 'url') {
414
+ StripeUrl = value;
415
+ }
416
+ return true;
417
+ },
418
+ lookup: ['bind', 'active', 'readyCount', 'complete', 'errors', 'instance', 'publicKey', 'styles', 'classes', 'url', 'pay', 'setup'],
419
+ })));
420
+ let bind = () => {
421
+ let resourceConcept = (0, inlinejs_1.GetGlobal)().GetConcept('resource');
422
+ if (StripeUrl && resourceConcept) {
423
+ resourceConcept.GetScript(StripeUrl).then(init);
424
+ }
425
+ else { //Resource not provided
426
+ init();
427
+ }
428
+ };
429
+ if (!options.manual) {
430
+ bind();
431
+ }
432
+ });
433
+ function StripeDirectiveHandlerCompact() {
434
+ (0, inlinejs_1.AddDirectiveHandler)(exports.StripeDirectiveHandler);
435
+ }
436
+ exports.StripeDirectiveHandlerCompact = StripeDirectiveHandlerCompact;
@@ -0,0 +1 @@
1
+ export * from './directive/stripe';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./directive/stripe"), exports);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ /// <reference types="stripe-v3" />
2
+ export interface IStripeStyle {
3
+ base?: stripe.elements.Style;
4
+ complete?: stripe.elements.Style;
5
+ empty?: stripe.elements.Style;
6
+ invalid?: stripe.elements.Style;
7
+ paymentRequestButton?: stripe.elements.PaymentRequestButtonStyleOptions;
8
+ }
9
+ export interface IStripeClass {
10
+ base?: string;
11
+ complete?: string;
12
+ empty?: string;
13
+ focus?: string;
14
+ invalid?: string;
15
+ webkitAutofill?: string;
16
+ }
17
+ export interface IStripeBillingDetails {
18
+ name: string;
19
+ email?: string;
20
+ phone?: string;
21
+ address?: string;
22
+ }
23
+ export declare const StripeDirectiveHandler: import("@benbraide/inlinejs").IDirectiveHandlerCallbackDetails;
24
+ export declare function StripeDirectiveHandlerCompact(): void;
@@ -0,0 +1,432 @@
1
+ import { FindComponentById, AddDirectiveHandler, CreateDirectiveHandlerCallback, EvaluateLater, GetGlobal, JournalError, JournalTry, AddChanges, BuildGetterProxyOptions, BuildProxyOptions, CreateInplaceProxy, BindEvent, ResolveOptions } from "@benbraide/inlinejs";
2
+ const StripeSpecialKeys = ['submit', 'save', 'name', 'email', 'phone', 'address'];
3
+ const StripeKeys = {
4
+ 'number': 'cardNumber',
5
+ 'expiry': 'cardExpiry',
6
+ 'cvc': 'cardCvc',
7
+ 'postal': 'postalCode',
8
+ 'zip': 'postalCode',
9
+ };
10
+ const StripeDirectiveName = 'stripe';
11
+ let StripePublicKey = '';
12
+ let StripeUrl = 'https://js.stripe.com/v3/';
13
+ let StripeStyles = null;
14
+ let StripeClasses = null;
15
+ export const StripeDirectiveHandler = CreateDirectiveHandlerCallback(StripeDirectiveName, ({ componentId, component, contextElement, expression, argKey, argOptions }) => {
16
+ if (BindEvent({ contextElement, expression,
17
+ component: (component || componentId),
18
+ key: StripeDirectiveName,
19
+ event: argKey,
20
+ defaultEvent: 'success',
21
+ eventWhitelist: ['error', 'before', 'after', 'ready', 'focus', 'complete'],
22
+ options: argOptions,
23
+ optionBlacklist: ['window', 'document', 'outside'],
24
+ })) {
25
+ return;
26
+ }
27
+ let resolvedComponent = (component || FindComponentById(componentId)), elementScope = resolvedComponent === null || resolvedComponent === void 0 ? void 0 : resolvedComponent.FindElementScope(contextElement);
28
+ if (!resolvedComponent || !elementScope) {
29
+ return JournalError('Failed to retrieve element scope.', `InlineJS.${StripeDirectiveName}`, contextElement);
30
+ }
31
+ let localKey = `\$${StripeDirectiveName}`, detailsKey = `#${StripeDirectiveName}`;
32
+ if (localKey in (elementScope.GetLocals() || {})) { //Already initialized
33
+ return;
34
+ }
35
+ if (StripeSpecialKeys.includes(argKey)) { //No Stripe binding
36
+ let details = resolvedComponent.FindElementLocalValue(contextElement, detailsKey, true);
37
+ if (details) {
38
+ details.specialMounts[argKey] = contextElement;
39
+ elementScope.SetLocal(localKey, CreateInplaceProxy(BuildGetterProxyOptions({
40
+ getter: (prop) => {
41
+ var _a;
42
+ if (prop === 'parent') {
43
+ return (contextElement.parentElement ? (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.FindElementLocalValue(contextElement.parentElement, localKey, true) : null);
44
+ }
45
+ },
46
+ lookup: ['parent'],
47
+ })));
48
+ }
49
+ return;
50
+ }
51
+ if (argKey in StripeKeys) { //Bind Stripe field
52
+ let details = resolvedComponent.FindElementLocalValue(contextElement, detailsKey, true);
53
+ if (!details) { //No parent
54
+ return;
55
+ }
56
+ let id = resolvedComponent.GenerateUniqueId(`${StripeDirectiveName}_proxy_`), field = {
57
+ name: argKey,
58
+ mount: contextElement,
59
+ element: details.elements.create(StripeKeys[argKey], {
60
+ style: (StripeStyles || undefined),
61
+ classes: (StripeClasses || undefined),
62
+ }),
63
+ ready: false,
64
+ complete: false,
65
+ error: undefined,
66
+ };
67
+ if (!field.element) {
68
+ return;
69
+ }
70
+ field.element.mount(contextElement);
71
+ details.fields.push(field);
72
+ elementScope.SetLocal(localKey, CreateInplaceProxy(BuildGetterProxyOptions({
73
+ getter: (prop) => {
74
+ var _a, _b, _c, _d;
75
+ if (prop === 'complete') {
76
+ (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
77
+ return field.complete;
78
+ }
79
+ if (prop === 'focused') {
80
+ (_b = FindComponentById(componentId)) === null || _b === void 0 ? void 0 : _b.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
81
+ return field.focused;
82
+ }
83
+ if (prop === 'error') {
84
+ (_c = FindComponentById(componentId)) === null || _c === void 0 ? void 0 : _c.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
85
+ return field.error;
86
+ }
87
+ if (prop === 'parent') {
88
+ return (contextElement.parentElement ? (_d = FindComponentById(componentId)) === null || _d === void 0 ? void 0 : _d.FindElementLocalValue(contextElement.parentElement, localKey, true) : null);
89
+ }
90
+ if (prop === 'clear') {
91
+ return () => {
92
+ if (field.element) {
93
+ field.element.clear();
94
+ }
95
+ };
96
+ }
97
+ if (prop === 'focus') {
98
+ return () => {
99
+ if (field.element) {
100
+ field.element.focus();
101
+ }
102
+ };
103
+ }
104
+ if (prop === 'blur') {
105
+ return () => {
106
+ if (field.element) {
107
+ field.element.blur();
108
+ }
109
+ };
110
+ }
111
+ },
112
+ lookup: ['complete', 'focused', 'error', 'parent', 'clear', 'focus', 'blur'],
113
+ })));
114
+ let fields = details.fields;
115
+ elementScope.AddUninitCallback(() => {
116
+ var _a;
117
+ (_a = field.element) === null || _a === void 0 ? void 0 : _a.destroy();
118
+ fields.splice(fields.indexOf(field), 1);
119
+ });
120
+ field.element.on('ready', () => {
121
+ if (!field.ready) {
122
+ field.ready = true;
123
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.ready`));
124
+ details.onReady();
125
+ }
126
+ });
127
+ field.element.on('change', (e) => {
128
+ var _a, _b;
129
+ if ((e === null || e === void 0 ? void 0 : e.complete) === field.complete) {
130
+ return;
131
+ }
132
+ field.complete = e === null || e === void 0 ? void 0 : e.complete;
133
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.complete`, {
134
+ detail: {
135
+ completed: e === null || e === void 0 ? void 0 : e.complete,
136
+ },
137
+ }));
138
+ if (field.complete) {
139
+ if (field.error) {
140
+ field.error = undefined;
141
+ AddChanges('set', `${id}.error`, 'error', (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
142
+ }
143
+ if (details.options.autofocus) { //Focus next if any
144
+ let index = details.fields.indexOf(field);
145
+ if (index != -1 && index < (details.fields.length - 1)) {
146
+ let nextField = details.fields[index + 1];
147
+ if (nextField.element) {
148
+ nextField.element.focus();
149
+ }
150
+ else if ('focus' in nextField.mount && typeof nextField.mount.focus === 'function') {
151
+ nextField.mount.focus();
152
+ }
153
+ }
154
+ else if (details.specialMounts.submit) {
155
+ details.specialMounts.submit.focus();
156
+ }
157
+ }
158
+ }
159
+ else if ((e === null || e === void 0 ? void 0 : e.error) && e.error.message !== field.error) {
160
+ field.error = e.error.message;
161
+ AddChanges('set', `${id}.error`, 'error', (_b = FindComponentById(componentId)) === null || _b === void 0 ? void 0 : _b.GetBackend().changes);
162
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.error`, {
163
+ detail: {
164
+ message: e.error.message,
165
+ },
166
+ }));
167
+ }
168
+ details.onChange();
169
+ });
170
+ field.element.on('focus', () => {
171
+ var _a;
172
+ if (!field.focused) {
173
+ field.focused = true;
174
+ AddChanges('set', `${id}.focused`, 'focused', (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
175
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.focus`, {
176
+ detail: {
177
+ focused: true,
178
+ },
179
+ }));
180
+ }
181
+ });
182
+ field.element.on('blur', () => {
183
+ var _a;
184
+ if (field.focused) {
185
+ field.focused = false;
186
+ AddChanges('set', `${id}.focused`, 'focused', (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
187
+ field.mount.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.focus`, {
188
+ detail: {
189
+ focused: false,
190
+ },
191
+ }));
192
+ }
193
+ });
194
+ return;
195
+ }
196
+ let stripeInstance = null, elements = null, backlog = new Array(), init = () => {
197
+ EvaluateLater({ componentId, contextElement, expression })((value) => {
198
+ stripeInstance = Stripe(value || StripePublicKey);
199
+ if (!stripeInstance) {
200
+ return JournalError('Failed to initialize stripe', `InlineJS.${StripeDirectiveName}.Init`, contextElement);
201
+ }
202
+ elements = stripeInstance.elements();
203
+ if (!elements) {
204
+ return JournalError('Failed to initialize stripe', `InlineJS.${StripeDirectiveName}.Init`, contextElement);
205
+ }
206
+ backlog.splice(0).forEach(callback => JournalTry(callback, `InlineJS.${StripeDirectiveName}.Init`, contextElement));
207
+ });
208
+ };
209
+ let fields = new Array(), specialMounts = {
210
+ submit: null,
211
+ save: null,
212
+ name: null,
213
+ email: null,
214
+ phone: null,
215
+ address: null,
216
+ };
217
+ let id = resolvedComponent.GenerateUniqueId(`${StripeDirectiveName}_proxy_`), options = ResolveOptions({
218
+ options: {
219
+ autofocus: false,
220
+ nexttick: false,
221
+ manual: false,
222
+ },
223
+ list: argOptions,
224
+ });
225
+ let active = false, complete = false, errorCount = 0, setComplete = (value) => {
226
+ var _a;
227
+ if (value != complete) {
228
+ complete = value;
229
+ AddChanges('set', `${id}.complete`, 'complete', (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
230
+ }
231
+ };
232
+ let details = { fields, specialMounts, options,
233
+ onReady: () => { var _a; return AddChanges('set', `${id}.readyCount`, 'readyCount', (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes); },
234
+ onChange: () => {
235
+ var _a;
236
+ setComplete(!fields.find(field => !field.complete));
237
+ let currentErrorCount = fields.reduce((prev, field) => (prev + (field.error ? 1 : 0)), 0);
238
+ if (currentErrorCount != errorCount) { //Error list changed
239
+ errorCount = currentErrorCount;
240
+ AddChanges('set', `${id}.errors`, 'errors', (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
241
+ }
242
+ },
243
+ };
244
+ let setActive = (value) => {
245
+ var _a;
246
+ if (value != active) {
247
+ active = value;
248
+ AddChanges('set', `${id}.active`, 'active', (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes);
249
+ }
250
+ };
251
+ let evaluate = EvaluateLater({ componentId, contextElement, expression }), onSuccess = (response) => {
252
+ var _a;
253
+ if (!response.error) {
254
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.success`, {
255
+ detail: {
256
+ intent: response.paymentIntent,
257
+ },
258
+ }));
259
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.after`));
260
+ setActive(false);
261
+ if (options.nexttick) {
262
+ (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes.AddNextTickHandler(() => evaluate());
263
+ }
264
+ else { //Immediate evaluation
265
+ evaluate();
266
+ }
267
+ }
268
+ else { //Error
269
+ onError(response.error.message || '');
270
+ }
271
+ };
272
+ let onError = (err) => {
273
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.error`, {
274
+ detail: {
275
+ type: 'host',
276
+ message: err,
277
+ },
278
+ }));
279
+ reportError('host', err);
280
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.after`));
281
+ setActive(false);
282
+ };
283
+ let reportError = (type, message) => {
284
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.error`, {
285
+ detail: { type, message },
286
+ }));
287
+ };
288
+ let getPaymentDetails = (paymentMethod, save) => {
289
+ var _a;
290
+ if (paymentMethod && typeof paymentMethod === 'string') {
291
+ return {
292
+ payment_method: paymentMethod,
293
+ setup_future_usage: (save ? 'off_session' : undefined),
294
+ };
295
+ }
296
+ let cardElement = (_a = fields.find(field => (field.name === 'number'))) === null || _a === void 0 ? void 0 : _a.element;
297
+ if (!cardElement) {
298
+ return null;
299
+ }
300
+ let billingDetails = {}, getBillingDetail = (key) => {
301
+ if (paymentMethod) {
302
+ return (paymentMethod[key] || (specialMounts[key] ? specialMounts[key].value : undefined));
303
+ }
304
+ return (specialMounts[key] ? specialMounts[key].value : undefined);
305
+ };
306
+ ['name', 'email', 'phone', 'address'].forEach((key) => {
307
+ if (key === 'address') {
308
+ billingDetails.address = {
309
+ line1: getBillingDetail(key),
310
+ };
311
+ }
312
+ else {
313
+ billingDetails[key] = getBillingDetail(key);
314
+ }
315
+ });
316
+ if (!save && specialMounts.save && specialMounts.save instanceof HTMLInputElement) {
317
+ save = specialMounts.save.checked;
318
+ }
319
+ return {
320
+ payment_method: {
321
+ card: cardElement,
322
+ billing_details: billingDetails,
323
+ },
324
+ setup_future_usage: (save ? 'off_session' : undefined),
325
+ };
326
+ };
327
+ let payOrSetup = (callback, hasPaymentMethod = false) => {
328
+ if (hasPaymentMethod || (complete && !fields.find(field => !!field.error))) {
329
+ setActive(true);
330
+ contextElement.dispatchEvent(new CustomEvent(`${StripeDirectiveName}.before`));
331
+ callback();
332
+ }
333
+ else { //Error
334
+ reportError('incomplete', 'Please fill in all required fields.');
335
+ }
336
+ };
337
+ elementScope.SetLocal(detailsKey, details);
338
+ elementScope.SetLocal(localKey, CreateInplaceProxy(BuildProxyOptions({
339
+ getter: (prop) => {
340
+ var _a, _b, _c, _d;
341
+ if (prop === 'bind') {
342
+ return () => {
343
+ if (!stripeInstance) {
344
+ bind();
345
+ }
346
+ };
347
+ }
348
+ if (prop === 'active') {
349
+ (_a = FindComponentById(componentId)) === null || _a === void 0 ? void 0 : _a.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
350
+ return active;
351
+ }
352
+ if (prop === 'readyCount') {
353
+ (_b = FindComponentById(componentId)) === null || _b === void 0 ? void 0 : _b.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
354
+ return fields.reduce((prev, field) => (prev + (field.ready ? 1 : 0)), 0);
355
+ }
356
+ if (prop === 'complete') {
357
+ (_c = FindComponentById(componentId)) === null || _c === void 0 ? void 0 : _c.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
358
+ return complete;
359
+ }
360
+ if (prop === 'errors') {
361
+ (_d = FindComponentById(componentId)) === null || _d === void 0 ? void 0 : _d.GetBackend().changes.AddGetAccess(`${id}.${prop}`);
362
+ return fields.filter(field => !!field.error).map(field => field.error);
363
+ }
364
+ if (prop === 'instance') {
365
+ return stripeInstance;
366
+ }
367
+ if (prop === 'publicKey') {
368
+ return StripePublicKey;
369
+ }
370
+ if (prop === 'styles') {
371
+ return StripeStyles;
372
+ }
373
+ if (prop === 'classes') {
374
+ return StripeClasses;
375
+ }
376
+ if (prop === 'url') {
377
+ return StripeUrl;
378
+ }
379
+ if (prop === 'pay') {
380
+ return (clientSecret, paymentMethod, save = false) => {
381
+ payOrSetup(() => {
382
+ let paymentDetails = getPaymentDetails(paymentMethod, save);
383
+ if (paymentDetails) {
384
+ stripeInstance === null || stripeInstance === void 0 ? void 0 : stripeInstance.confirmCardPayment(clientSecret, paymentDetails).then(onSuccess).catch(onError);
385
+ }
386
+ }, (!!paymentMethod && typeof paymentMethod === 'string'));
387
+ };
388
+ }
389
+ if (prop === 'setup') {
390
+ return (clientSecret, paymentMethod, save = false) => {
391
+ payOrSetup(() => {
392
+ let paymentDetails = getPaymentDetails(paymentMethod, save);
393
+ if (paymentDetails) {
394
+ stripeInstance === null || stripeInstance === void 0 ? void 0 : stripeInstance.confirmCardSetup(clientSecret, paymentDetails).then(onSuccess).catch(onError);
395
+ }
396
+ }, (!!paymentMethod && typeof paymentMethod === 'string'));
397
+ };
398
+ }
399
+ },
400
+ setter: (prop, value) => {
401
+ if (prop === 'publicKey') {
402
+ StripePublicKey = value;
403
+ }
404
+ else if (prop === 'styles') {
405
+ StripeStyles = value;
406
+ }
407
+ else if (prop === 'classes') {
408
+ StripeClasses = value;
409
+ }
410
+ else if (prop === 'url') {
411
+ StripeUrl = value;
412
+ }
413
+ return true;
414
+ },
415
+ lookup: ['bind', 'active', 'readyCount', 'complete', 'errors', 'instance', 'publicKey', 'styles', 'classes', 'url', 'pay', 'setup'],
416
+ })));
417
+ let bind = () => {
418
+ let resourceConcept = GetGlobal().GetConcept('resource');
419
+ if (StripeUrl && resourceConcept) {
420
+ resourceConcept.GetScript(StripeUrl).then(init);
421
+ }
422
+ else { //Resource not provided
423
+ init();
424
+ }
425
+ };
426
+ if (!options.manual) {
427
+ bind();
428
+ }
429
+ });
430
+ export function StripeDirectiveHandlerCompact() {
431
+ AddDirectiveHandler(StripeDirectiveHandler);
432
+ }
@@ -0,0 +1 @@
1
+ export * from './directive/stripe';
@@ -0,0 +1 @@
1
+ export * from './directive/stripe';
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@benbraide/inlinejs-stripe",
3
+ "version": "1.0.0",
4
+ "description": "Run javascript code by embedding them in your HTML using the element as context.",
5
+ "main": "./lib/common/index.js",
6
+ "module": "./lib/esm/index.js",
7
+ "files": [
8
+ "lib/"
9
+ ],
10
+ "scripts": {
11
+ "test": "mocha -r jsdom-global/register lib/**/*.spec.js",
12
+ "ts-test": "mocha -r ts-node/register -r jsdom-global/register src/**/*.spec.ts",
13
+ "dbg-test": "mocha --inspect-brk -r ts-node/register -r jsdom-global/register src/**/*.spec.ts",
14
+ "tsc": "tsc -p ./tsconfig.json && tsc -p ./tsconfig.esm.json",
15
+ "prepublishOnly": "npm run tsc",
16
+ "tailwind": "postcss tailwind.css -o plublic/style.css"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/benbraide/inlinejs-stripe.git"
21
+ },
22
+ "keywords": [
23
+ "javascript",
24
+ "reactive",
25
+ "framework",
26
+ "html",
27
+ "attribute",
28
+ "embed"
29
+ ],
30
+ "author": "Benebo Braide",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/benbraide/inlinejs-stripe/issues"
34
+ },
35
+ "homepage": "https://github.com/benbraide/inlinejs-stripe#readme",
36
+ "devDependencies": {
37
+ "@testing-library/dom": "^7.31.2",
38
+ "@testing-library/user-event": "^13.1.9",
39
+ "@types/chai": "^4.2.18",
40
+ "@types/mocha": "^8.2.2",
41
+ "@types/stripe-v3": "^3.1.26",
42
+ "chai": "^4.3.4",
43
+ "jsdom": "^16.6.0",
44
+ "jsdom-global": "^3.0.2",
45
+ "mocha": "^9.0.0",
46
+ "ts-loader": "^9.2.3",
47
+ "ts-node": "^10.0.0",
48
+ "webpack": "^5.41.0"
49
+ },
50
+ "dependencies": {
51
+ "@benbraide/inlinejs": "^1.0.8",
52
+ "webpack-cli": "^4.7.2"
53
+ }
54
+ }