@rebilly/framepay-react 2.0.0 → 2.1.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.
Files changed (80) hide show
  1. package/.env.example +2 -0
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +7 -6
  4. package/env.js +7 -0
  5. package/package.json +23 -17
  6. package/src/index.spec.ts +19 -0
  7. package/src/index.ts +42 -0
  8. package/src/lib/components/elements/applepay-element.tsx +36 -0
  9. package/src/lib/components/elements/bank-element.spec.tsx +119 -0
  10. package/src/lib/components/elements/bank-element.tsx +74 -0
  11. package/src/lib/components/elements/base-element.tsx +100 -0
  12. package/src/lib/components/elements/card-element.spec.tsx +113 -0
  13. package/src/lib/components/elements/card-element.tsx +73 -0
  14. package/src/lib/components/elements/googlepay-element.tsx +36 -0
  15. package/src/lib/components/elements/iban-element.spec.tsx +104 -0
  16. package/src/lib/components/elements/iban-element.tsx +75 -0
  17. package/src/lib/components/elements/paypal-element.tsx +36 -0
  18. package/src/lib/components/injector.spec.tsx +162 -0
  19. package/src/lib/components/injector.tsx +495 -0
  20. package/src/lib/components/provider.spec.tsx +98 -0
  21. package/src/lib/components/provider.tsx +111 -0
  22. package/src/lib/constants.ts +43 -0
  23. package/src/lib/context.ts +24 -0
  24. package/src/lib/dom-util.ts +35 -0
  25. package/src/lib/framepay-error.ts +59 -0
  26. package/src/lib/get-rebilly-api.ts +11 -0
  27. package/test/e2e/assets/prop-types.js +849 -0
  28. package/test/e2e/assets/react-0.14.0.js +18759 -0
  29. package/test/e2e/assets/react-15.0.0.js +19309 -0
  30. package/test/e2e/assets/react-16.js +3318 -0
  31. package/test/e2e/assets/react-17.js +3357 -0
  32. package/test/e2e/assets/react-18.js +3342 -0
  33. package/test/e2e/assets/react-dom-0.14.0.js +42 -0
  34. package/test/e2e/assets/react-dom-15.0.0.js +42 -0
  35. package/test/e2e/assets/react-dom-16.js +25147 -0
  36. package/test/e2e/assets/react-dom-17.js +26292 -0
  37. package/test/e2e/assets/react-dom-18.js +29869 -0
  38. package/test/e2e/cypress-support.js +2 -0
  39. package/test/e2e/cypress.d.ts +1 -0
  40. package/test/e2e/fixtures/apple-pay.html +15 -0
  41. package/test/e2e/fixtures/apple-pay.js +63 -0
  42. package/test/e2e/fixtures/bank-separate.html +12 -0
  43. package/test/e2e/fixtures/bank-separate.js +323 -0
  44. package/test/e2e/fixtures/card-separate-brands.html +12 -0
  45. package/test/e2e/fixtures/card-separate-brands.js +260 -0
  46. package/test/e2e/fixtures/card-separate-rebilly-fields.html +12 -0
  47. package/test/e2e/fixtures/card-separate-rebilly-fields.js +281 -0
  48. package/test/e2e/fixtures/card-separate.html +12 -0
  49. package/test/e2e/fixtures/card-separate.js +227 -0
  50. package/test/e2e/fixtures/checkout-combined.html +12 -0
  51. package/test/e2e/fixtures/checkout-combined.js +199 -0
  52. package/test/e2e/fixtures/google-pay.html +15 -0
  53. package/test/e2e/fixtures/google-pay.js +63 -0
  54. package/test/e2e/fixtures/iban.html +12 -0
  55. package/test/e2e/fixtures/iban.js +239 -0
  56. package/test/e2e/fixtures/multiple-methods.html +12 -0
  57. package/test/e2e/fixtures/multiple-methods.js +470 -0
  58. package/test/e2e/fixtures/nav.js +20 -0
  59. package/test/e2e/fixtures/paypal.html +15 -0
  60. package/test/e2e/fixtures/paypal.js +62 -0
  61. package/test/e2e/fixtures/style.css +55 -0
  62. package/test/e2e/fixtures/util.js +71 -0
  63. package/test/e2e/local-server.mjs +12 -0
  64. package/test/e2e/server.mjs +43 -0
  65. package/test/e2e/specs/bank-separate.cy.ts +27 -0
  66. package/test/e2e/specs/card-separate-brands.cy.ts +70 -0
  67. package/test/e2e/specs/card-separate.cy.ts +27 -0
  68. package/test/e2e/specs/checkout-combined.cy.ts +24 -0
  69. package/test/e2e/specs/google-pay.cy.ts +13 -0
  70. package/test/e2e/specs/iban.cy.ts +17 -0
  71. package/test/e2e/specs/multiple-methods.cy.ts +130 -0
  72. package/test/e2e/specs/paypal.cy.ts +13 -0
  73. package/test/e2e/specs/react-version.cy.ts +12 -0
  74. package/test/e2e/switch-react-version.js +42 -0
  75. package/test/e2e/tsconfig.json +8 -0
  76. package/test/unit/jest.config.js +29 -0
  77. package/test/unit/specs/declaration-mock.spec.tsx +143 -0
  78. package/tsconfig.json +31 -0
  79. package/tsconfig.spec.json +13 -0
  80. package/tslint.json +36 -0
@@ -0,0 +1,470 @@
1
+ import React, { Component } from 'react';
2
+ import ReactDOM from 'react-dom';
3
+
4
+ import { FramePayProvider, withFramePay } from '../../../build';
5
+ import { deepMerge, prettyDebugRender, ReactVersion } from './util';
6
+ import './style.css';
7
+
8
+ const params = {
9
+ publishableKey: 'pk_sandbox_S95ATjj4hXZs-T9QpZq1ENl2tDSrUkCGv98utc9',
10
+ organizationId: '5977150c-1c97-4dd4-9860-6bb2bab070b4',
11
+ websiteId: 'demo.com',
12
+ transactionData: {
13
+ amount: 10,
14
+ currency: 'USD',
15
+ label: 'Purchase label 1',
16
+ },
17
+ };
18
+
19
+ const defaultEvents = () => ({
20
+ card: {
21
+ onReady: false,
22
+ onChange: false,
23
+ onFocus: false,
24
+ onBlur: false
25
+ },
26
+ bankAccountType: {
27
+ onReady: false,
28
+ onChange: false,
29
+ onFocus: false,
30
+ onBlur: false
31
+ },
32
+ bankAccountNumber: {
33
+ onReady: false,
34
+ onChange: false,
35
+ onFocus: false,
36
+ onBlur: false
37
+ },
38
+ bankRoutingNumber: {
39
+ onReady: false,
40
+ onChange: false,
41
+ onFocus: false,
42
+ onBlur: false
43
+ },
44
+ iban: {
45
+ onReady: false,
46
+ onChange: false,
47
+ onFocus: false,
48
+ onBlur: false
49
+ }
50
+ });
51
+
52
+ class PaymentFormComponent extends Component {
53
+ constructor(props) {
54
+ super(props);
55
+ this.state = {
56
+ paymentElements: {
57
+ // {element: method}
58
+ card: 'payment-card',
59
+ bank: 'ach',
60
+ iban: 'ach',
61
+ googlepay: 'googlepay'
62
+ },
63
+ paymentElement: 'card',
64
+ billingAddress: {
65
+ firstName: 'first-name-value',
66
+ lastName: 'last-name-value',
67
+ country: 'GB'
68
+ }
69
+ };
70
+ this.handleSubmit = this.handleSubmit.bind(this);
71
+ }
72
+
73
+ handleSubmit(e) {
74
+ e.preventDefault();
75
+ /**
76
+ *
77
+ * @see https://rebilly.github.io/framepay-docs/reference/rebilly.html#rebilly-createtoken
78
+ *
79
+ */
80
+ const billingAddress = {
81
+ ...this.state.billingAddress
82
+ };
83
+
84
+ this.props.Rebilly.createToken(this.formNode, {
85
+ // method: this.state.paymentElements[this.state.paymentElement],
86
+ billingAddress
87
+ })
88
+ .then(data => {
89
+ this.props.onEvent({
90
+ token: {
91
+ error: false,
92
+ data: { ...data, method: data.method }
93
+ }
94
+ });
95
+ })
96
+ .catch(err => {
97
+ this.props.onEvent({ token: { error: true, data: err } });
98
+ });
99
+ }
100
+
101
+ render() {
102
+ const elements = Object.keys(this.state.paymentElements);
103
+
104
+ const changeElement = (element) => {
105
+ this.setState({
106
+ paymentElement: element,
107
+ });
108
+ // Clear events
109
+ this.props.onEvent({
110
+ events: {
111
+ ...defaultEvents(),
112
+ },
113
+ })
114
+ }
115
+
116
+ return (
117
+ <div className="example-2">
118
+ <ul>
119
+ {elements.map(element => (
120
+ <li key={`payment-method-${element}`}>
121
+ <button
122
+ id={`set-active-element-${element}`}
123
+ onClick={() => changeElement(element)}
124
+ >
125
+ {element} - isActive:
126
+ {String(element === this.state.paymentElement)}
127
+ </button>
128
+ </li>
129
+ ))}
130
+ </ul>
131
+ <form
132
+ id="form"
133
+ ref={node => (this.formNode = node)}
134
+ method="post"
135
+ onSubmit={this.handleSubmit}
136
+ >
137
+ <fieldset>
138
+ <div className="field">
139
+ <input
140
+ type="text"
141
+ name="firstName"
142
+ placeholder="First Name"
143
+ defaultValue={
144
+ this.state.billingAddress.firstName
145
+ }
146
+ onChange={e => {
147
+ this.props.onEvent({
148
+ billingAddress: {
149
+ firstName: e.target.value
150
+ }
151
+ });
152
+ }}
153
+ />
154
+ </div>
155
+ <div className="field">
156
+ <input
157
+ type="text"
158
+ name="lastName"
159
+ placeholder="Last Name"
160
+ defaultValue={
161
+ this.state.billingAddress.lastName
162
+ }
163
+ onChange={e => {
164
+ this.props.onEvent({
165
+ billingAddress: {
166
+ lastName: e.target.value
167
+ }
168
+ });
169
+ }}
170
+ />
171
+ </div>
172
+ <hr />
173
+ <div className="field" id="field-CardElement">
174
+ {this.state.paymentElement === 'card' && (
175
+ <this.props.CardElement
176
+ onReady={() =>
177
+ this.props.onEvent({
178
+ events: {
179
+ card: { onReady: true }
180
+ }
181
+ })
182
+ }
183
+ onChange={data =>
184
+ this.props.onEvent({
185
+ events: {
186
+ card: {
187
+ onChange: {
188
+ ...data,
189
+ error: data.error || ''
190
+ }
191
+ }
192
+ }
193
+ })
194
+ }
195
+ onFocus={() =>
196
+ this.props.onEvent({
197
+ events: {
198
+ card: { onFocus: true }
199
+ }
200
+ })
201
+ }
202
+ onBlur={() =>
203
+ this.props.onEvent({
204
+ events: {
205
+ card: { onBlur: true }
206
+ }
207
+ })
208
+ }
209
+ />
210
+ )}
211
+ {this.state.paymentElement === 'googlepay' && (
212
+ <this.props.GooglePayElement />
213
+ )}
214
+ {this.state.paymentElement === 'bank' && (
215
+ <div>
216
+ <div
217
+ className="field"
218
+ id="field-BankAccountTypeElement"
219
+ >
220
+ <label>Account Type</label>
221
+ <this.props.BankAccountTypeElement
222
+ onReady={() =>
223
+ this.props.onEvent({
224
+ events: {
225
+ bankAccountType: {
226
+ onReady: true
227
+ }
228
+ }
229
+ })
230
+ }
231
+ onChange={data =>
232
+ this.props.onEvent({
233
+ events: {
234
+ bankAccountType: {
235
+ onChange: {
236
+ ...data,
237
+ error:
238
+ data.error ||
239
+ ''
240
+ }
241
+ }
242
+ }
243
+ })
244
+ }
245
+ onFocus={() =>
246
+ this.props.onEvent({
247
+ events: {
248
+ bankAccountType: {
249
+ onFocus: true
250
+ }
251
+ }
252
+ })
253
+ }
254
+ onBlur={() =>
255
+ this.props.onEvent({
256
+ events: {
257
+ bankAccountType: {
258
+ onBlur: true
259
+ }
260
+ }
261
+ })
262
+ }
263
+ />
264
+ </div>
265
+ <div
266
+ className="field"
267
+ id="field-BankRoutingNumberElement"
268
+ >
269
+ <label>Routing Number</label>
270
+ <this.props.BankRoutingNumberElement
271
+ onReady={() =>
272
+ this.props.onEvent({
273
+ events: {
274
+ bankRoutingNumber: {
275
+ onReady: true
276
+ }
277
+ }
278
+ })
279
+ }
280
+ onChange={data =>
281
+ this.props.onEvent({
282
+ events: {
283
+ bankRoutingNumber: {
284
+ onChange: {
285
+ ...data,
286
+ error:
287
+ data.error ||
288
+ ''
289
+ }
290
+ }
291
+ }
292
+ })
293
+ }
294
+ onFocus={() =>
295
+ this.props.onEvent({
296
+ events: {
297
+ bankRoutingNumber: {
298
+ onFocus: true
299
+ }
300
+ }
301
+ })
302
+ }
303
+ onBlur={() =>
304
+ this.props.onEvent({
305
+ events: {
306
+ bankRoutingNumber: {
307
+ onBlur: true
308
+ }
309
+ }
310
+ })
311
+ }
312
+ />
313
+ </div>
314
+ <div
315
+ className="field"
316
+ id="field-BankAccountNumberElement"
317
+ >
318
+ <label>Account Number</label>
319
+ <this.props.BankAccountNumberElement
320
+ onReady={() =>
321
+ this.props.onEvent({
322
+ events: {
323
+ bankAccountNumber: {
324
+ onReady: true
325
+ }
326
+ }
327
+ })
328
+ }
329
+ onChange={data =>
330
+ this.props.onEvent({
331
+ events: {
332
+ bankAccountNumber: {
333
+ onChange: {
334
+ ...data,
335
+ error:
336
+ data.error ||
337
+ ''
338
+ }
339
+ }
340
+ }
341
+ })
342
+ }
343
+ onFocus={() =>
344
+ this.props.onEvent({
345
+ events: {
346
+ bankAccountNumber: {
347
+ onFocus: true
348
+ }
349
+ }
350
+ })
351
+ }
352
+ onBlur={() =>
353
+ this.props.onEvent({
354
+ events: {
355
+ bankAccountNumber: {
356
+ onBlur: true
357
+ }
358
+ }
359
+ })
360
+ }
361
+ />
362
+ </div>
363
+ </div>
364
+ )}
365
+ {this.state.paymentElement === 'iban' && (
366
+ <div>
367
+ <div
368
+ className="field"
369
+ id="field-IBANElement"
370
+ >
371
+ <label>IBAN NUmber</label>
372
+ <this.props.IBANElement
373
+ onReady={() =>
374
+ this.props.onEvent({
375
+ events: {
376
+ iban: {
377
+ onReady: true
378
+ }
379
+ }
380
+ })
381
+ }
382
+ onChange={data =>
383
+ this.props.onEvent({
384
+ events: {
385
+ iban: {
386
+ onChange: {
387
+ ...data,
388
+ error:
389
+ data.error ||
390
+ ''
391
+ }
392
+ }
393
+ }
394
+ })
395
+ }
396
+ onFocus={() =>
397
+ this.props.onEvent({
398
+ events: {
399
+ iban: {
400
+ onFocus: true
401
+ }
402
+ }
403
+ })
404
+ }
405
+ onBlur={() =>
406
+ this.props.onEvent({
407
+ events: {
408
+ iban: {
409
+ onBlur: true
410
+ }
411
+ }
412
+ })
413
+ }
414
+ />
415
+ </div>
416
+ </div>
417
+ )}
418
+ </div>
419
+ </fieldset>
420
+
421
+ <hr />
422
+ <button id="submit">Make Payment</button>
423
+ </form>
424
+ </div>
425
+ );
426
+ }
427
+ }
428
+
429
+ const PaymentForm = withFramePay(PaymentFormComponent);
430
+
431
+ class App extends Component {
432
+ constructor(props) {
433
+ super(props);
434
+ this.state = {
435
+ events: {
436
+ ...defaultEvents()
437
+ },
438
+ token: {
439
+ error: null,
440
+ data: null
441
+ }
442
+ };
443
+ }
444
+
445
+ deepUpdateState(data) {
446
+ this.setState(prevState => {
447
+ return deepMerge(prevState, data);
448
+ });
449
+ }
450
+
451
+ render() {
452
+ return (
453
+ <FramePayProvider injectStyle {...params} onTokenReady={(token) => this.deepUpdateState({ error: false, data: token })}>
454
+ <div>
455
+ {ReactVersion()}
456
+ <div>
457
+ <div className="flex-wrapper">
458
+ {prettyDebugRender(this.state)}
459
+ <PaymentForm
460
+ onEvent={data => this.deepUpdateState(data)}
461
+ />
462
+ </div>
463
+ </div>
464
+ </div>
465
+ </FramePayProvider>
466
+ );
467
+ }
468
+ }
469
+
470
+ ReactDOM.render(<App />, document.getElementById('app'));
@@ -0,0 +1,20 @@
1
+ setTimeout(() => {
2
+ const node = document.createElement('ul');
3
+ [
4
+ 'apple-pay',
5
+ 'bank-separate',
6
+ 'card-separate',
7
+ 'card-separate-rebilly-fields',
8
+ 'card-separate-brands',
9
+ 'checkout-combined',
10
+ 'google-pay',
11
+ 'iban',
12
+ 'multiple-methods',
13
+ 'paypal',
14
+ ]
15
+ .forEach(route => {
16
+ node.innerHTML += `<li><a href="/${route}.html">${route}</a></li>`;
17
+ });
18
+
19
+ document.body.appendChild(node);
20
+ }, 1000);
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <title>Test Paypal</title>
7
+ </head>
8
+
9
+ <body>
10
+ <div id="app"></div>
11
+ <script src="paypal.js"></script>
12
+ <script src="nav.js"></script>
13
+ </body>
14
+
15
+ </html>
@@ -0,0 +1,62 @@
1
+ import React, { Component } from 'react';
2
+ import ReactDOM from 'react-dom';
3
+
4
+ import {
5
+ FramePayProvider,
6
+ withFramePayPaypalComponent
7
+ } from '../../../build';
8
+ import { prettyDebugRender, ReactVersion } from './util';
9
+ import './style.css';
10
+
11
+ const params = {
12
+ publishableKey: 'pk_sandbox_S95ATjj4hXZs-T9QpZq1ENl2tDSrUkCGv98utc9',
13
+ organizationId: '5977150c-1c97-4dd4-9860-6bb2bab070b4',
14
+ websiteId: 'demo.com',
15
+ transactionData: {
16
+ amount: 10,
17
+ currency: 'USD',
18
+ }
19
+ };
20
+
21
+ class App extends Component {
22
+
23
+ constructor(props) {
24
+ super(props);
25
+ this.state = {
26
+ token: null,
27
+ };
28
+ }
29
+
30
+ render() {
31
+ return (
32
+ <FramePayProvider
33
+ injectStyle
34
+ {...params}
35
+ onReady={() => {
36
+ console.log('FramePayProvider.onReady');
37
+ }}
38
+ onError={err => {
39
+ console.log('FramePayProvider.onError', err);
40
+ }}
41
+ onTokenReady={token => this.setState({ token })}
42
+ >
43
+ <div>
44
+ {ReactVersion()}
45
+ <div>
46
+ <h3>FramePay version: {this.props.Rebilly.version}</h3>
47
+ <div className="flex-wrapper">
48
+ {prettyDebugRender(this.state)}
49
+ <this.props.PaypalElement />
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </FramePayProvider>
54
+ );
55
+ }
56
+ }
57
+
58
+ const WrappedApp = withFramePayPaypalComponent(App);
59
+ ReactDOM.render(
60
+ <WrappedApp />,
61
+ document.getElementById('app')
62
+ );
@@ -0,0 +1,55 @@
1
+ iframe {
2
+ border: 0;
3
+ outline: none;
4
+ }
5
+
6
+ form {
7
+ padding: 5px;
8
+ border: 1px solid #ddd;
9
+ }
10
+
11
+ fieldset {
12
+ border: 0;
13
+ }
14
+
15
+ .field {
16
+ margin: 5px 0;
17
+ }
18
+
19
+ input[type="text"] {
20
+ display: block;
21
+ width: 100%;
22
+ padding: 1px;
23
+ }
24
+
25
+ button#submit {
26
+ display: block;
27
+ padding: 5px;
28
+ margin: 5px 0;
29
+ }
30
+
31
+ button#submit:hover {
32
+ cursor: pointer;
33
+ }
34
+
35
+ .flex-wrapper {
36
+ display: flex;
37
+ width: 100%;
38
+ }
39
+
40
+ .flex-wrapper > div:first-child {
41
+ font-size: 80%;
42
+ line-height: normal;
43
+ }
44
+
45
+ .flex-wrapper > div {
46
+ width: 50%;
47
+ padding: 5px;
48
+ border: 1px solid #ddd;
49
+ margin: 5px;
50
+ }
51
+
52
+ #pre,
53
+ #data {
54
+ background: #ddd;
55
+ }
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+
3
+ export const prettyRenderState = (data, parentKey) => {
4
+ if (data !== null && typeof data === 'object') {
5
+ return <ul>
6
+ {Object.keys(data).map((key, index) => {
7
+
8
+ const dataKey = `${parentKey ? parentKey + '-' : ''}${key}`;
9
+ const value = data[key];
10
+
11
+ if (value === null) {
12
+ return null;
13
+ }
14
+
15
+ return <li
16
+ key={`state-render-${key}-${index}`}
17
+ id={`key-${key}`}>
18
+ <span
19
+ className="key"><b>{key}</b>:</span>
20
+ &nbsp;
21
+ {prettyRenderState(value, dataKey)}
22
+ </li>;
23
+ })}
24
+ </ul>;
25
+ }
26
+
27
+ if (data === null) {
28
+ return null;
29
+ }
30
+
31
+ const val = typeof data === 'object' ? JSON.stringify(data) : data;
32
+
33
+ return <span>
34
+ <span id={`${parentKey}-${val}`} data-value={val}/>
35
+ <span id={parentKey} data-value={val}>{String(val)}</span>
36
+ </span>;
37
+ };
38
+
39
+ export const prettyDebugRender = (state) => {
40
+ return (<div>
41
+ <div>
42
+ <pre id="pre">{JSON.stringify(state, null, 2)}</pre>
43
+ </div>
44
+ <div>
45
+ <div id="data">{prettyRenderState(state)}</div>
46
+ </div>
47
+ </div>);
48
+ };
49
+
50
+
51
+ export const deepMerge = (target, source) => {
52
+ for (const key of Object.keys(source)) {
53
+ if (source[key] instanceof Object) {
54
+ source[key] = source[key] || {};
55
+ target[key] = target[key] || {};
56
+ Object.assign(
57
+ source[key],
58
+ deepMerge(target[key], source[key])
59
+ );
60
+ }
61
+ }
62
+
63
+ return Object.assign(target || {}, source);
64
+ };
65
+
66
+
67
+ export const ReactVersion = () => {
68
+ return (<div>
69
+ <h3>React version: <span id="react-version" data-version={React.version}>{React.version}</span></h3>
70
+ </div>);
71
+ };