@edx/frontend-app-subscription-manage 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Main.js +1 -1
- package/dist/Main.js.map +1 -1
- package/dist/app.js +3 -0
- package/dist/app.js.map +1 -1
- package/dist/checkoutPage/CheckoutPage.d.ts +3 -0
- package/dist/checkoutPage/CheckoutPage.js +24 -0
- package/dist/checkoutPage/CheckoutPage.js.map +1 -0
- package/dist/components/BrandMarks/BrandMarks.d.ts +3 -0
- package/dist/components/BrandMarks/BrandMarks.js +9 -0
- package/dist/components/BrandMarks/BrandMarks.js.map +1 -0
- package/dist/components/CartSummary/CartSummary.d.ts +21 -0
- package/dist/components/CartSummary/CartSummary.js +30 -0
- package/dist/components/CartSummary/CartSummary.js.map +1 -0
- package/dist/components/CartSummary/Messages.d.ts +73 -0
- package/dist/components/CartSummary/Messages.js +76 -0
- package/dist/components/CartSummary/Messages.js.map +1 -0
- package/dist/components/Checkout/Checkout.d.ts +4 -0
- package/dist/components/Checkout/Checkout.js +37 -0
- package/dist/components/Checkout/Checkout.js.map +1 -0
- package/dist/components/CheckoutGate/CheckoutGate.d.ts +9 -0
- package/dist/components/CheckoutGate/CheckoutGate.js +34 -0
- package/dist/components/CheckoutGate/CheckoutGate.js.map +1 -0
- package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.d.ts +9 -0
- package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.js +28 -0
- package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.js.map +1 -0
- package/dist/components/CourseEnrollmentCard/Messages.d.ts +33 -0
- package/dist/components/CourseEnrollmentCard/Messages.js +35 -0
- package/dist/components/CourseEnrollmentCard/Messages.js.map +1 -0
- package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.d.ts +13 -0
- package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.js +11 -0
- package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.js.map +1 -0
- package/dist/components/EnrollmentSummary/EnrollmentSummary.d.ts +15 -0
- package/dist/components/EnrollmentSummary/EnrollmentSummary.js +21 -0
- package/dist/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -0
- package/dist/components/EnrollmentSummary/Messages.d.ts +33 -0
- package/dist/components/EnrollmentSummary/Messages.js +35 -0
- package/dist/components/EnrollmentSummary/Messages.js.map +1 -0
- package/dist/components/PaymentFields/PaymentFields.d.ts +9 -0
- package/dist/components/PaymentFields/PaymentFields.js +7 -0
- package/dist/components/PaymentFields/PaymentFields.js.map +1 -0
- package/dist/components/PaymentForm/Messages.d.ts +88 -0
- package/dist/components/PaymentForm/Messages.js +95 -0
- package/dist/components/PaymentForm/Messages.js.map +1 -0
- package/dist/components/PaymentForm/PaymentForm.d.ts +21 -0
- package/dist/components/PaymentForm/PaymentForm.js +69 -0
- package/dist/components/PaymentForm/PaymentForm.js.map +1 -0
- package/dist/components/StatusScreens/Messages.d.ts +103 -0
- package/dist/components/StatusScreens/Messages.js +108 -0
- package/dist/components/StatusScreens/Messages.js.map +1 -0
- package/dist/components/StatusScreens/StatusScreens.d.ts +23 -0
- package/dist/components/StatusScreens/StatusScreens.js +25 -0
- package/dist/components/StatusScreens/StatusScreens.js.map +1 -0
- package/dist/constants.d.ts +59 -0
- package/dist/constants.js +275 -0
- package/dist/constants.js.map +1 -1
- package/dist/data/hooks/keys.d.ts +14 -0
- package/dist/data/hooks/keys.js +15 -0
- package/dist/data/hooks/keys.js.map +1 -0
- package/dist/data/hooks/useCheckout.d.ts +57 -0
- package/dist/data/hooks/useCheckout.js +249 -0
- package/dist/data/hooks/useCheckout.js.map +1 -0
- package/dist/data/hooks/useCheckoutGate.d.ts +15 -0
- package/dist/data/hooks/useCheckoutGate.js +54 -0
- package/dist/data/hooks/useCheckoutGate.js.map +1 -0
- package/dist/data/hooks/useCheckoutMutations.d.ts +16 -0
- package/dist/data/hooks/useCheckoutMutations.js +131 -0
- package/dist/data/hooks/useCheckoutMutations.js.map +1 -0
- package/dist/data/hooks/useCheckoutQueries.d.ts +35 -0
- package/dist/data/hooks/useCheckoutQueries.js +110 -0
- package/dist/data/hooks/useCheckoutQueries.js.map +1 -0
- package/dist/data/services/subs/urls.d.ts +2 -0
- package/dist/data/services/subs/urls.js +4 -0
- package/dist/data/services/subs/urls.js.map +1 -0
- package/dist/routes.d.ts +2 -1
- package/dist/routes.js +33 -4
- package/dist/routes.js.map +1 -1
- package/dist/style.scss +13 -0
- package/dist/styles/components/brandMarks.scss +54 -0
- package/dist/styles/components/cartSummary.scss +18 -0
- package/dist/styles/components/checkout.scss +29 -0
- package/dist/styles/components/checkoutStepper.scss +9 -0
- package/dist/styles/components/courseEnrollmentCard.scss +38 -0
- package/dist/styles/components/edxUnlimitedBanner.scss +138 -0
- package/dist/styles/components/enrollmentSummary.scss +13 -0
- package/dist/styles/components/paymentFields.scss +15 -0
- package/dist/styles/components/paymentForm.scss +105 -0
- package/dist/styles/components/statusScreens.scss +29 -0
- package/dist/styles/components/successScreen.scss +0 -0
- package/dist/types.d.ts +222 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/auth.d.ts +7 -0
- package/dist/utils/auth.js +28 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/billingDetails.d.ts +30 -0
- package/dist/utils/billingDetails.js +47 -0
- package/dist/utils/billingDetails.js.map +1 -0
- package/dist/utils/bootstrapApi.d.ts +20 -0
- package/dist/utils/bootstrapApi.js +142 -0
- package/dist/utils/bootstrapApi.js.map +1 -0
- package/dist/utils/cartApi.d.ts +16 -0
- package/dist/utils/cartApi.js +118 -0
- package/dist/utils/cartApi.js.map +1 -0
- package/dist/utils/checkout.d.ts +6 -0
- package/dist/utils/checkout.js +28 -0
- package/dist/utils/checkout.js.map +1 -0
- package/dist/utils/fetchActiveSubscriptions.d.ts +4 -0
- package/dist/utils/fetchActiveSubscriptions.js +36 -0
- package/dist/utils/fetchActiveSubscriptions.js.map +1 -0
- package/dist/utils/fetchBillingPlans.d.ts +2 -0
- package/dist/utils/fetchBillingPlans.js +48 -0
- package/dist/utils/fetchBillingPlans.js.map +1 -0
- package/dist/utils/graphqlClient.d.ts +16 -0
- package/dist/utils/graphqlClient.js +39 -0
- package/dist/utils/graphqlClient.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +17 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/provision.d.ts +25 -0
- package/dist/utils/provision.js +107 -0
- package/dist/utils/provision.js.map +1 -0
- package/dist/utils/queries.d.ts +35 -0
- package/dist/utils/queries.js +168 -0
- package/dist/utils/queries.js.map +1 -0
- package/dist/utils/stripe.d.ts +7 -0
- package/dist/utils/stripe.js +43 -0
- package/dist/utils/stripe.js.map +1 -0
- package/package.json +2 -2
- package/dist/example/ExamplePage.d.ts +0 -2
- package/dist/example/ExamplePage.js +0 -6
- package/dist/example/ExamplePage.js.map +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
|
|
2
|
+
.edx-banner {
|
|
3
|
+
background: #0a1628;
|
|
4
|
+
border-radius: 10px;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: stretch;
|
|
8
|
+
min-height: 120px;
|
|
9
|
+
position: relative;
|
|
10
|
+
|
|
11
|
+
&__content {
|
|
12
|
+
flex: 1 1 0;
|
|
13
|
+
padding: 28px 32px 28px 28px;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
gap: 10px;
|
|
18
|
+
z-index: 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&__eyebrow {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&__headline {
|
|
27
|
+
margin: 0;
|
|
28
|
+
color: #fff;
|
|
29
|
+
font-size: clamp(1.1rem, 2.2vw, 1.55rem);
|
|
30
|
+
font-weight: 700;
|
|
31
|
+
line-height: 1.25;
|
|
32
|
+
letter-spacing: -0.01em;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&__body {
|
|
36
|
+
margin: 0;
|
|
37
|
+
color: rgba(255, 255, 255, 0.78);
|
|
38
|
+
font-size: clamp(0.78rem, 1.3vw, 0.88rem);
|
|
39
|
+
line-height: 1.55;
|
|
40
|
+
max-width: 540px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&__cta-wrapper {
|
|
44
|
+
margin-top: 4px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&__cta {
|
|
48
|
+
display: inline-block;
|
|
49
|
+
background: #e2522a;
|
|
50
|
+
color: #fff;
|
|
51
|
+
font-weight: 700;
|
|
52
|
+
font-size: 0.82rem;
|
|
53
|
+
padding: 9px 20px;
|
|
54
|
+
border-radius: 6px;
|
|
55
|
+
text-decoration: none;
|
|
56
|
+
letter-spacing: 0.01em;
|
|
57
|
+
transition: background 0.15s;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
|
|
60
|
+
&:hover {
|
|
61
|
+
background: #c94420;
|
|
62
|
+
color: #fff;
|
|
63
|
+
text-decoration: none;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&__bullet {
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
width: 28px;
|
|
72
|
+
height: 28px;
|
|
73
|
+
border-radius: 50%;
|
|
74
|
+
background: rgba(255, 255, 255, 0.15);
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
margin-right: 10px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&__badge {
|
|
80
|
+
display: inline-flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 0;
|
|
83
|
+
vertical-align: middle;
|
|
84
|
+
position: relative;
|
|
85
|
+
top: -2px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&__badge-edx {
|
|
89
|
+
background: #fff;
|
|
90
|
+
border-radius: 4px 0 0 4px;
|
|
91
|
+
padding: 2px 5px 2px 6px;
|
|
92
|
+
font-weight: 800;
|
|
93
|
+
font-size: 0.78em;
|
|
94
|
+
letter-spacing: -0.02em;
|
|
95
|
+
color: #000;
|
|
96
|
+
line-height: 1.15;
|
|
97
|
+
font-style: normal;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&__badge-unlimited {
|
|
101
|
+
background: #000;
|
|
102
|
+
border-radius: 0 4px 4px 0;
|
|
103
|
+
padding: 2px 6px 2px 4px;
|
|
104
|
+
font-weight: 600;
|
|
105
|
+
font-size: 0.64em;
|
|
106
|
+
letter-spacing: 0.02em;
|
|
107
|
+
color: #c5b8ff;
|
|
108
|
+
line-height: 1.15;
|
|
109
|
+
font-style: normal;
|
|
110
|
+
text-transform: lowercase;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&__photo {
|
|
114
|
+
flex-shrink: 0;
|
|
115
|
+
width: clamp(120px, 22%, 240px);
|
|
116
|
+
position: relative;
|
|
117
|
+
overflow: hidden;
|
|
118
|
+
|
|
119
|
+
img {
|
|
120
|
+
position: absolute;
|
|
121
|
+
inset: 0;
|
|
122
|
+
width: 100%;
|
|
123
|
+
height: 100%;
|
|
124
|
+
object-fit: cover;
|
|
125
|
+
object-position: center top;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
&__placeholder {
|
|
131
|
+
flex-shrink: 0;
|
|
132
|
+
width: clamp(100px, 20%, 200px);
|
|
133
|
+
background: rgba(255, 255, 255, 0.06);
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: center;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.enrolled__banner {
|
|
2
|
+
div:first-child {
|
|
3
|
+
border-top-left-radius: var(--pgn-size-border-radius-base);
|
|
4
|
+
border-top-right-radius: var(--pgn-size-border-radius-base);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
div:last-child {
|
|
8
|
+
border: 1px solid var(--pgn-color-light-400);
|
|
9
|
+
border-top: 0;
|
|
10
|
+
border-bottom-left-radius: var(--pgn-size-border-radius-base);
|
|
11
|
+
border-bottom-right-radius: var(--pgn-size-border-radius-base);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.payment--field {
|
|
2
|
+
.card__expiry,
|
|
3
|
+
.card__cvc {
|
|
4
|
+
width: 10.75rem;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.form-control {
|
|
8
|
+
border-radius: 5px;
|
|
9
|
+
border: 1px solid #E6E6E6;
|
|
10
|
+
background: #FFF;
|
|
11
|
+
|
|
12
|
+
/* default/box-shadow-default */
|
|
13
|
+
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.02), 0 1px 1px 0 rgba(0, 0, 0, 0.03);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// styles/components/paymentForm.scss
|
|
2
|
+
|
|
3
|
+
.payment-form {
|
|
4
|
+
&__heading {
|
|
5
|
+
font-size: 1.1rem;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
&__help {
|
|
9
|
+
a {
|
|
10
|
+
text-decoration: underline;
|
|
11
|
+
text-underline-offset: 0.2rem;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ── Payment method accordion ─────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
&__method {
|
|
18
|
+
border-radius: 8px;
|
|
19
|
+
border: 1px solid #E7E9EF;
|
|
20
|
+
background: #FFF;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
background: #F6F8FA;
|
|
23
|
+
&--selected {
|
|
24
|
+
background-color: white;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Header row: radio + icon(s) + name — full-width clickable label
|
|
29
|
+
&__method-header {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: 0.6rem;
|
|
33
|
+
padding: 0.9rem 1rem;
|
|
34
|
+
margin: 0;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
width: 100%;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
font-size: 0.95rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&__radio {
|
|
42
|
+
flex-shrink: 0;
|
|
43
|
+
width: 1.1rem;
|
|
44
|
+
height: 1.1rem;
|
|
45
|
+
accent-color: #000000;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&__method-name {
|
|
50
|
+
flex: 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Expanded card panel — no extra heading, just fields
|
|
54
|
+
&__card-panel {
|
|
55
|
+
padding: 0 1rem 1rem;
|
|
56
|
+
background: #fff;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Icon badges for Google Pay / PayPal
|
|
60
|
+
&__method-icon {
|
|
61
|
+
display: inline-flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
width: 1.5rem;
|
|
65
|
+
height: 1.5rem;
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
color: #fff;
|
|
68
|
+
font-weight: 800;
|
|
69
|
+
font-size: 0.7rem;
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
|
|
72
|
+
&--gpay {
|
|
73
|
+
background: #4285f4;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
&--paypal {
|
|
77
|
+
background: #003087;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.payment__field--country {
|
|
82
|
+
select {
|
|
83
|
+
border-radius: 5px;
|
|
84
|
+
border: 1px solid #E6E6E6;
|
|
85
|
+
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.02), 0 1px 1px 0 rgba(0, 0, 0, 0.03);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Submit button ────────────────────────────────────────────────────────
|
|
90
|
+
&__submit {
|
|
91
|
+
border-radius: 2rem;
|
|
92
|
+
font-weight: 700;
|
|
93
|
+
padding: 0.75rem 1.5rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&__terms {
|
|
97
|
+
line-height: 1.6;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@media (--pgn-size-breakpoint-min-width-lg) {
|
|
102
|
+
.payment-form {
|
|
103
|
+
padding: 32px;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// styles/components/successScreen.scss
|
|
2
|
+
|
|
3
|
+
.success-screen {
|
|
4
|
+
&__headline {
|
|
5
|
+
font-size: clamp(1.6rem, 3vw, 2.2rem);
|
|
6
|
+
color: #1a3a2a; // dark green as in design
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
&__ctas {
|
|
10
|
+
flex-wrap: wrap;
|
|
11
|
+
gap: 0.75rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&__cta-primary {
|
|
15
|
+
border-radius: 2rem;
|
|
16
|
+
font-weight: 700;
|
|
17
|
+
padding: 0.65rem 1.5rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&__benefits {
|
|
21
|
+
list-style: disc;
|
|
22
|
+
padding-left: 1.25rem;
|
|
23
|
+
|
|
24
|
+
li {
|
|
25
|
+
color: var(--gray-700, #495057);
|
|
26
|
+
line-height: 1.6;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
File without changes
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import type { Status, Phase } from './constants';
|
|
2
|
+
export type { Status, Phase };
|
|
3
|
+
export interface TwouCustomerContact {
|
|
4
|
+
email?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TwouCustomerParty {
|
|
7
|
+
paName: string;
|
|
8
|
+
indFirstName?: string;
|
|
9
|
+
indLastName?: string;
|
|
10
|
+
paContacts: {
|
|
11
|
+
edges: {
|
|
12
|
+
node: TwouCustomerContact;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface TwouCustomer {
|
|
17
|
+
uid: string;
|
|
18
|
+
parId: string;
|
|
19
|
+
parParty: TwouCustomerParty;
|
|
20
|
+
}
|
|
21
|
+
export type GqlRunner = (query: string, variables?: Record<string, unknown>, retry?: boolean) => Promise<any>;
|
|
22
|
+
export interface Cart {
|
|
23
|
+
biId: string;
|
|
24
|
+
uid: string;
|
|
25
|
+
}
|
|
26
|
+
export interface CartIds {
|
|
27
|
+
customerUid: string;
|
|
28
|
+
salableUid: string;
|
|
29
|
+
billingPlanUid: string;
|
|
30
|
+
}
|
|
31
|
+
export type BillingCycle = 'monthly' | 'annual';
|
|
32
|
+
export type BillingPeriod = 'MONTHLY' | 'ANNUAL' | 'QUARTERLY' | string;
|
|
33
|
+
export interface BillingPlanOwnershipModel {
|
|
34
|
+
uid: string;
|
|
35
|
+
omName: string;
|
|
36
|
+
}
|
|
37
|
+
export interface BillingPlan {
|
|
38
|
+
uid: string;
|
|
39
|
+
stripePriceId: string;
|
|
40
|
+
/** Price amount e.g. "39.99" */
|
|
41
|
+
amount: string;
|
|
42
|
+
/** Currency code e.g. "USD" */
|
|
43
|
+
currency: string;
|
|
44
|
+
isTemplate: boolean;
|
|
45
|
+
ownershipModel: BillingPlanOwnershipModel;
|
|
46
|
+
}
|
|
47
|
+
export interface SubscriptionDetails {
|
|
48
|
+
biId: string;
|
|
49
|
+
trialStart: string | null;
|
|
50
|
+
trialEnd: string | null;
|
|
51
|
+
renewalType: string | null;
|
|
52
|
+
cardLast4: string | null;
|
|
53
|
+
cardBrand: string | null;
|
|
54
|
+
stripePaymentMethodId: string | null;
|
|
55
|
+
planName: string | null;
|
|
56
|
+
}
|
|
57
|
+
export interface CurrentUserSubscriberResult {
|
|
58
|
+
isSubscriber: boolean;
|
|
59
|
+
subscriptionBiId: string | null;
|
|
60
|
+
status: string | null;
|
|
61
|
+
subscriptionStartDate: string | null;
|
|
62
|
+
currentPeriodEnd: string | null;
|
|
63
|
+
}
|
|
64
|
+
/** Course shown on checkout / post-checkout when arriving via ?course_run_key=. */
|
|
65
|
+
export interface CourseDetail {
|
|
66
|
+
maId: string;
|
|
67
|
+
maName: string;
|
|
68
|
+
maDescription: string | null;
|
|
69
|
+
tShortDescription: string | null;
|
|
70
|
+
imageUrl: string | null;
|
|
71
|
+
tCourseKey: string | null;
|
|
72
|
+
tBrandText: string | null;
|
|
73
|
+
tPrimarySubjectArea: string | null;
|
|
74
|
+
tLevelType: string | null;
|
|
75
|
+
tLanguage: string | null;
|
|
76
|
+
tPacingType: string | null;
|
|
77
|
+
tDurationLow: number | null;
|
|
78
|
+
tDurationHigh: number | null;
|
|
79
|
+
tDurationUnit: string | null;
|
|
80
|
+
tEffortLow: number | null;
|
|
81
|
+
tEffortHigh: number | null;
|
|
82
|
+
tEffortUnit: string | null;
|
|
83
|
+
tUrlCourse: string | null;
|
|
84
|
+
tB2cSubscriptionInclusion: boolean | null;
|
|
85
|
+
tHasOfacRestrictions: boolean | null;
|
|
86
|
+
tCatalogActive: boolean | null;
|
|
87
|
+
}
|
|
88
|
+
/** Returned by getBillingPlans — all available plans for the product. */
|
|
89
|
+
export interface BillingPlansResult {
|
|
90
|
+
plans: BillingPlan[];
|
|
91
|
+
}
|
|
92
|
+
/** Returned by getSalableUid — the product uid and its ownership models. */
|
|
93
|
+
export interface ProductInfo {
|
|
94
|
+
uid: string;
|
|
95
|
+
maName: string;
|
|
96
|
+
ownershipModels: {
|
|
97
|
+
uid: string;
|
|
98
|
+
omName: string;
|
|
99
|
+
}[];
|
|
100
|
+
}
|
|
101
|
+
export interface CartStatusResult {
|
|
102
|
+
status: Status | null;
|
|
103
|
+
stripeSubscriptionId: string | null;
|
|
104
|
+
lastComment: string;
|
|
105
|
+
}
|
|
106
|
+
export interface CartBillingAddress {
|
|
107
|
+
tFirstName: string;
|
|
108
|
+
tLastName: string;
|
|
109
|
+
upaLine1: string;
|
|
110
|
+
upaCity: string;
|
|
111
|
+
upaPostcode: string;
|
|
112
|
+
gadStateOrProvince: string;
|
|
113
|
+
gadCountry: {
|
|
114
|
+
iso2Code: string;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export interface UpdateCartItemArgs {
|
|
118
|
+
cartItemUid: string;
|
|
119
|
+
billingAddress?: CartBillingAddress;
|
|
120
|
+
paymentMethodId?: string;
|
|
121
|
+
}
|
|
122
|
+
/** Auto-renewal consent captured on TwouCart at checkout confirmation (SUBS-228). */
|
|
123
|
+
export interface CheckoutConsent {
|
|
124
|
+
/** Main auto-renewal checkbox — always required at submit. */
|
|
125
|
+
tConsentRecurring: boolean;
|
|
126
|
+
/** UK/CA resident checkbox; null when that checkbox is not shown. */
|
|
127
|
+
tConsentUkCa: boolean | null;
|
|
128
|
+
/** ISO-8601 timestamp when consent was captured. */
|
|
129
|
+
tConsentAt: string;
|
|
130
|
+
/** Exact disclosure text shown to the learner (audit trail). */
|
|
131
|
+
tConsentText: string;
|
|
132
|
+
}
|
|
133
|
+
export interface UpdateCartConsentArgs {
|
|
134
|
+
cartUid: string;
|
|
135
|
+
consent: CheckoutConsent;
|
|
136
|
+
}
|
|
137
|
+
export interface BillingInfo {
|
|
138
|
+
firstName: string;
|
|
139
|
+
lastName: string;
|
|
140
|
+
city: string;
|
|
141
|
+
zip: string;
|
|
142
|
+
country: string;
|
|
143
|
+
line1: string;
|
|
144
|
+
state: string;
|
|
145
|
+
}
|
|
146
|
+
export interface CheckoutClient {
|
|
147
|
+
gql: GqlRunner;
|
|
148
|
+
setToken: (token: string) => void;
|
|
149
|
+
createCart: (vars: CartIds) => Promise<Cart>;
|
|
150
|
+
getCartItem: (biId: string) => Promise<{
|
|
151
|
+
cartUid: string;
|
|
152
|
+
cartItemUid: string;
|
|
153
|
+
}>;
|
|
154
|
+
updateCartItem: (args: UpdateCartItemArgs) => Promise<void>;
|
|
155
|
+
updateCartConsent: (args: UpdateCartConsentArgs) => Promise<void>;
|
|
156
|
+
placeOrder: (cartUid: string) => Promise<unknown>;
|
|
157
|
+
getCartStatus: (biId: string) => Promise<CartStatusResult>;
|
|
158
|
+
findSubscriptionBiId: (biId: string) => Promise<SubscriptionDetails | null>;
|
|
159
|
+
getSalableUid: (maId: string) => Promise<ProductInfo>;
|
|
160
|
+
getBillingPlans: () => Promise<BillingPlansResult>;
|
|
161
|
+
getActiveSubscriptions: () => Promise<{
|
|
162
|
+
active: number;
|
|
163
|
+
}>;
|
|
164
|
+
isCurrentUserSubscriber: () => Promise<CurrentUserSubscriberResult | null>;
|
|
165
|
+
getCourseDetail: (courseKey: string) => Promise<CourseDetail | null>;
|
|
166
|
+
}
|
|
167
|
+
export interface DevAuth {
|
|
168
|
+
iamUrl: string;
|
|
169
|
+
clientId: string;
|
|
170
|
+
clientSecret: string;
|
|
171
|
+
}
|
|
172
|
+
export interface StripeElementChangeEvent {
|
|
173
|
+
error?: {
|
|
174
|
+
message?: string;
|
|
175
|
+
};
|
|
176
|
+
[key: string]: unknown;
|
|
177
|
+
}
|
|
178
|
+
export interface StripeElementLike {
|
|
179
|
+
mount: (node: HTMLElement) => void;
|
|
180
|
+
on: (event: string, handler: (e: StripeElementChangeEvent) => void) => void;
|
|
181
|
+
unmount?: () => void;
|
|
182
|
+
}
|
|
183
|
+
export interface StripeElementsLike {
|
|
184
|
+
create: (type: string, options?: Record<string, unknown>) => StripeElementLike;
|
|
185
|
+
}
|
|
186
|
+
export interface StripeError {
|
|
187
|
+
message?: string;
|
|
188
|
+
code?: string;
|
|
189
|
+
}
|
|
190
|
+
export interface PaymentMethodResult {
|
|
191
|
+
paymentMethod?: {
|
|
192
|
+
id: string;
|
|
193
|
+
};
|
|
194
|
+
error?: StripeError;
|
|
195
|
+
}
|
|
196
|
+
export interface ConfirmResult {
|
|
197
|
+
error?: StripeError;
|
|
198
|
+
setupIntent?: unknown;
|
|
199
|
+
paymentIntent?: unknown;
|
|
200
|
+
}
|
|
201
|
+
export interface CreatePaymentMethodParams {
|
|
202
|
+
type: 'card';
|
|
203
|
+
card: StripeElementLike;
|
|
204
|
+
billing_details?: Record<string, unknown>;
|
|
205
|
+
}
|
|
206
|
+
export interface StripeLike {
|
|
207
|
+
elements: () => StripeElementsLike;
|
|
208
|
+
createPaymentMethod: (params: CreatePaymentMethodParams) => Promise<PaymentMethodResult>;
|
|
209
|
+
confirmCardSetup: (clientSecret: string) => Promise<ConfirmResult>;
|
|
210
|
+
confirmCardPayment: (clientSecret: string) => Promise<ConfirmResult>;
|
|
211
|
+
}
|
|
212
|
+
declare global {
|
|
213
|
+
interface Window {
|
|
214
|
+
Stripe?: (publishableKey: string) => StripeLike;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
export interface TrialDateRange {
|
|
218
|
+
/** e.g. "June 22, 2026" */
|
|
219
|
+
displayStart: string;
|
|
220
|
+
/** e.g. "June 29, 2026" */
|
|
221
|
+
displayEnd: string;
|
|
222
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Status, Phase } from './constants';\n\nexport type { Status, Phase };\n\n// --- Bootstrap domain --------------------------------------------------------\nexport interface TwouCustomerContact {\n email?: string,\n}\n\nexport interface TwouCustomerParty {\n paName: string,\n indFirstName?: string,\n indLastName?: string,\n paContacts: { edges: { node: TwouCustomerContact }[] },\n}\n\nexport interface TwouCustomer {\n uid: string,\n parId: string,\n parParty: TwouCustomerParty,\n}\n\nexport type GqlRunner = (query: string, variables?: Record<string, unknown>, retry?: boolean) => Promise<any>;\n\n// --- Cart domain -------------------------------------------------------------\nexport interface Cart {\n biId: string,\n uid: string,\n}\n\nexport interface CartIds {\n customerUid: string,\n salableUid: string,\n billingPlanUid: string,\n}\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport type BillingPeriod = 'MONTHLY' | 'ANNUAL' | 'QUARTERLY' | string;\n\nexport interface BillingPlanOwnershipModel {\n uid: string,\n omName: string,\n}\n\nexport interface BillingPlan {\n uid: string,\n stripePriceId: string,\n /** Price amount e.g. \"39.99\" */\n amount: string,\n /** Currency code e.g. \"USD\" */\n currency: string,\n isTemplate: boolean,\n ownershipModel: BillingPlanOwnershipModel,\n}\n\nexport interface SubscriptionDetails {\n biId: string, // \"BI301ZDAD6CMICG2\"\n trialStart: string | null,\n trialEnd: string | null,\n renewalType: string | null, // \"Annual\" | \"Monthly\"\n cardLast4: string | null, // \"4242\"\n cardBrand: string | null, // \"visa\" | \"mastercard\" etc\n stripePaymentMethodId: string | null,\n planName: string | null, // \"Monthly Subscription\"\n}\n\nexport interface CurrentUserSubscriberResult {\n isSubscriber: boolean,\n subscriptionBiId: string | null,\n status: string | null,\n subscriptionStartDate: string | null,\n currentPeriodEnd: string | null,\n}\n\n/** Course shown on checkout / post-checkout when arriving via ?course_run_key=. */\nexport interface CourseDetail {\n maId: string,\n maName: string,\n maDescription: string | null,\n tShortDescription: string | null,\n imageUrl: string | null,\n tCourseKey: string | null,\n tBrandText: string | null,\n tPrimarySubjectArea: string | null,\n tLevelType: string | null,\n tLanguage: string | null,\n tPacingType: string | null,\n tDurationLow: number | null,\n tDurationHigh: number | null,\n tDurationUnit: string | null,\n tEffortLow: number | null,\n tEffortHigh: number | null,\n tEffortUnit: string | null,\n tUrlCourse: string | null,\n tB2cSubscriptionInclusion: boolean | null,\n tHasOfacRestrictions: boolean | null,\n tCatalogActive: boolean | null,\n}\n\n/** Returned by getBillingPlans — all available plans for the product. */\nexport interface BillingPlansResult {\n plans: BillingPlan[],\n}\n\n/** Returned by getSalableUid — the product uid and its ownership models. */\nexport interface ProductInfo {\n uid: string,\n maName: string,\n ownershipModels: { uid: string, omName: string }[],\n}\n\nexport interface CartStatusResult {\n status: Status | null,\n stripeSubscriptionId: string | null,\n lastComment: string,\n}\n\nexport interface CartBillingAddress {\n tFirstName: string,\n tLastName: string,\n upaLine1: string,\n upaCity: string,\n upaPostcode: string,\n gadStateOrProvince: string,\n gadCountry: { iso2Code: string },\n}\n\nexport interface UpdateCartItemArgs {\n cartItemUid: string,\n billingAddress?: CartBillingAddress,\n paymentMethodId?: string,\n}\n\n/** Auto-renewal consent captured on TwouCart at checkout confirmation (SUBS-228). */\nexport interface CheckoutConsent {\n /** Main auto-renewal checkbox — always required at submit. */\n tConsentRecurring: boolean,\n /** UK/CA resident checkbox; null when that checkbox is not shown. */\n tConsentUkCa: boolean | null,\n /** ISO-8601 timestamp when consent was captured. */\n tConsentAt: string,\n /** Exact disclosure text shown to the learner (audit trail). */\n tConsentText: string,\n}\n\nexport interface UpdateCartConsentArgs {\n cartUid: string,\n consent: CheckoutConsent,\n}\n\nexport interface BillingInfo {\n firstName: string,\n lastName: string,\n city: string,\n zip: string,\n country: string,\n line1: string,\n state: string,\n}\n\n// --- Checkout client surface -------------------------------------------------\nexport interface CheckoutClient {\n gql: GqlRunner,\n setToken: (token: string) => void,\n createCart: (vars: CartIds) => Promise<Cart>,\n getCartItem: (biId: string) => Promise<{ cartUid: string, cartItemUid: string }>,\n updateCartItem: (args: UpdateCartItemArgs) => Promise<void>,\n updateCartConsent: (args: UpdateCartConsentArgs) => Promise<void>,\n placeOrder: (cartUid: string) => Promise<unknown>,\n getCartStatus: (biId: string) => Promise<CartStatusResult>,\n findSubscriptionBiId: (biId: string) => Promise<SubscriptionDetails | null>,\n getSalableUid: (maId: string) => Promise<ProductInfo>,\n getBillingPlans: () => Promise<BillingPlansResult>,\n getActiveSubscriptions: () => Promise<{ active: number }>,\n isCurrentUserSubscriber: () => Promise<CurrentUserSubscriberResult | null>,\n getCourseDetail: (courseKey: string) => Promise<CourseDetail | null>,\n}\n\nexport interface DevAuth {\n iamUrl: string,\n clientId: string,\n clientSecret: string,\n}\n\n// --- Minimal Stripe.js types -------------------------------------------------\n// Just the surface this module uses. If you depend on `@stripe/stripe-js`,\n// you can replace these with its richer types.\nexport interface StripeElementChangeEvent {\n error?: { message?: string },\n [key: string]: unknown,\n}\n\nexport interface StripeElementLike {\n mount: (node: HTMLElement) => void,\n on: (event: string, handler: (e: StripeElementChangeEvent) => void) => void,\n unmount?: () => void,\n}\n\nexport interface StripeElementsLike {\n create: (type: string, options?: Record<string, unknown>) => StripeElementLike,\n}\n\nexport interface StripeError {\n message?: string,\n code?: string,\n}\n\nexport interface PaymentMethodResult {\n paymentMethod?: { id: string },\n error?: StripeError,\n}\n\nexport interface ConfirmResult {\n error?: StripeError,\n setupIntent?: unknown,\n paymentIntent?: unknown,\n}\n\nexport interface CreatePaymentMethodParams {\n type: 'card',\n card: StripeElementLike,\n billing_details?: Record<string, unknown>,\n}\n\nexport interface StripeLike {\n elements: () => StripeElementsLike,\n createPaymentMethod: (params: CreatePaymentMethodParams) => Promise<PaymentMethodResult>,\n confirmCardSetup: (clientSecret: string) => Promise<ConfirmResult>,\n confirmCardPayment: (clientSecret: string) => Promise<ConfirmResult>,\n}\n\ndeclare global {\n interface Window {\n Stripe?: (publishableKey: string) => StripeLike,\n }\n}\n\nexport interface TrialDateRange {\n /** e.g. \"June 22, 2026\" */\n displayStart: string,\n /** e.g. \"June 29, 2026\" */\n displayEnd: string,\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api/index.ts — composes the pieces into a single checkout client and
|
|
3
|
+
* re-exports the API utilities.
|
|
4
|
+
*/
|
|
5
|
+
import { createGqlClient } from './graphqlClient';
|
|
6
|
+
import * as cart from './cartApi';
|
|
7
|
+
import * as bootstrap from './bootstrapApi';
|
|
8
|
+
export function createCheckoutClient({ apiUrl }) {
|
|
9
|
+
const { gql, setToken } = createGqlClient({ apiUrl });
|
|
10
|
+
const fetchStatus = (biId) => cart.getCartStatus(gql, biId);
|
|
11
|
+
return {
|
|
12
|
+
gql,
|
|
13
|
+
setToken,
|
|
14
|
+
createCart: (vars) => cart.createCart(gql, vars),
|
|
15
|
+
getCartItem: (biId) => cart.getCartItem(gql, biId),
|
|
16
|
+
updateCartItem: (args) => cart.updateCartItem(gql, args),
|
|
17
|
+
updateCartConsent: (args) => cart.updateCartConsent(gql, args),
|
|
18
|
+
placeOrder: (cartUid) => cart.placeOrder(gql, cartUid),
|
|
19
|
+
getCartStatus: fetchStatus,
|
|
20
|
+
findSubscriptionBiId: (biId) => cart.findSubscriptionBiId(gql, biId),
|
|
21
|
+
getSalableUid: (maId) => bootstrap.getSalableUid(gql, maId),
|
|
22
|
+
getBillingPlans: () => bootstrap.getBillingPlans(gql),
|
|
23
|
+
getActiveSubscriptions: () => bootstrap.getActiveSubscriptions(gql),
|
|
24
|
+
isCurrentUserSubscriber: () => bootstrap.isCurrentUserSubscriber(gql),
|
|
25
|
+
getCourseDetail: (courseKey) => bootstrap.getCourseDetail(gql, courseKey),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAU5C,MAAM,UAAU,oBAAoB,CAClC,EAAE,MAAM,EAAwB;IAEhC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAEpE,OAAO;QACL,GAAG;QACH,QAAQ;QACR,UAAU,EAAE,CAAC,IAAa,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;QACzD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC;QAClD,cAAc,EAAE,CAAC,IAAwB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;QAC5E,iBAAiB,EAAE,CAAC,IAA2B,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC;QACrF,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;QACtD,aAAa,EAAE,WAAW;QAC1B,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC;QACpE,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC;QAC3D,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC;QACrD,sBAAsB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,sBAAsB,CAAC,GAAG,CAAC;QACnE,uBAAuB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,GAAG,CAAC;QACrE,eAAe,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * api/index.ts — composes the pieces into a single checkout client and\n * re-exports the API utilities.\n */\nimport { createGqlClient } from './graphqlClient';\nimport * as cart from './cartApi';\nimport * as bootstrap from './bootstrapApi';\nimport type {\n CheckoutClient, CartIds, UpdateCartItemArgs, UpdateCartConsentArgs,\n} from '../types';\n\ninterface CheckoutClientConfig {\n apiUrl: string,\n confirmIntentUrl?: string,\n}\n\nexport function createCheckoutClient(\n { apiUrl }: CheckoutClientConfig,\n): CheckoutClient {\n const { gql, setToken } = createGqlClient({ apiUrl });\n const fetchStatus = (biId: string) => cart.getCartStatus(gql, biId);\n\n return {\n gql,\n setToken,\n createCart: (vars: CartIds) => cart.createCart(gql, vars),\n getCartItem: (biId) => cart.getCartItem(gql, biId),\n updateCartItem: (args: UpdateCartItemArgs) => cart.updateCartItem(gql, args),\n updateCartConsent: (args: UpdateCartConsentArgs) => cart.updateCartConsent(gql, args),\n placeOrder: (cartUid) => cart.placeOrder(gql, cartUid),\n getCartStatus: fetchStatus,\n findSubscriptionBiId: (biId) => cart.findSubscriptionBiId(gql, biId),\n getSalableUid: (maId) => bootstrap.getSalableUid(gql, maId),\n getBillingPlans: () => bootstrap.getBillingPlans(gql),\n getActiveSubscriptions: () => bootstrap.getActiveSubscriptions(gql),\n isCurrentUserSubscriber: () => bootstrap.isCurrentUserSubscriber(gql),\n getCourseDetail: (courseKey) => bootstrap.getCourseDetail(gql, courseKey),\n };\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { BillingPlan, BillingCycle } from '../types';
|
|
2
|
+
export interface PlanPrice {
|
|
3
|
+
uid: string;
|
|
4
|
+
stripePriceId: string;
|
|
5
|
+
amount: string;
|
|
6
|
+
currency: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* getPlanPrice — returns the price for the selected billing cycle.
|
|
11
|
+
*
|
|
12
|
+
* Matches plans by looking for "annual"/"year" or "month" in omName.
|
|
13
|
+
* Falls back to the first plan if no match is found.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* getPlanPrice(billingPlans, 'monthly')
|
|
17
|
+
* // → { uid: '...', amount: '11', currency: 'USD', label: '$11 USD/month' }
|
|
18
|
+
*
|
|
19
|
+
* getPlanPrice(billingPlans, 'annual')
|
|
20
|
+
* // → { uid: '...', amount: '111', currency: 'USD', label: '$111 USD/year' }
|
|
21
|
+
*/
|
|
22
|
+
export declare function getPlanPrice(plans: BillingPlan[], cycle: BillingCycle): PlanPrice | null;
|
|
23
|
+
/**
|
|
24
|
+
* getRenewalLabel — returns a "Then $X/month" style label for CartSummary.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* getRenewalLabel(billingPlans, 'monthly') // → "Then $11 USD/month"
|
|
28
|
+
* getRenewalLabel(billingPlans, 'annual') // → "Then $111 USD/year"
|
|
29
|
+
*/
|
|
30
|
+
export declare function getRenewalLabel(plans: BillingPlan[], cycle: BillingCycle): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getPlanPrice — returns the price for the selected billing cycle.
|
|
3
|
+
*
|
|
4
|
+
* Matches plans by looking for "annual"/"year" or "month" in omName.
|
|
5
|
+
* Falls back to the first plan if no match is found.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* getPlanPrice(billingPlans, 'monthly')
|
|
9
|
+
* // → { uid: '...', amount: '11', currency: 'USD', label: '$11 USD/month' }
|
|
10
|
+
*
|
|
11
|
+
* getPlanPrice(billingPlans, 'annual')
|
|
12
|
+
* // → { uid: '...', amount: '111', currency: 'USD', label: '$111 USD/year' }
|
|
13
|
+
*/
|
|
14
|
+
export function getPlanPrice(plans, cycle) {
|
|
15
|
+
var _a;
|
|
16
|
+
if (!plans.length)
|
|
17
|
+
return null;
|
|
18
|
+
const isAnnual = cycle === 'annual';
|
|
19
|
+
const match = (_a = plans.find((p) => {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
const name = (_b = (_a = p.ownershipModel.omName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
|
|
22
|
+
return isAnnual
|
|
23
|
+
? name.includes('annual') || name.includes('year')
|
|
24
|
+
: name.includes('month');
|
|
25
|
+
})) !== null && _a !== void 0 ? _a : plans[0];
|
|
26
|
+
return {
|
|
27
|
+
uid: match.uid,
|
|
28
|
+
stripePriceId: match.stripePriceId,
|
|
29
|
+
amount: match.amount,
|
|
30
|
+
currency: match.currency,
|
|
31
|
+
label: `$${match.amount} ${match.currency}/${isAnnual ? 'year' : 'month'}`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* getRenewalLabel — returns a "Then $X/month" style label for CartSummary.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* getRenewalLabel(billingPlans, 'monthly') // → "Then $11 USD/month"
|
|
39
|
+
* getRenewalLabel(billingPlans, 'annual') // → "Then $111 USD/year"
|
|
40
|
+
*/
|
|
41
|
+
export function getRenewalLabel(plans, cycle) {
|
|
42
|
+
const price = getPlanPrice(plans, cycle);
|
|
43
|
+
if (!price)
|
|
44
|
+
return '';
|
|
45
|
+
return `Then $${price.amount} ${price.currency}/${cycle === 'annual' ? 'year' : 'month'}`;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=billingDetails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billingDetails.js","sourceRoot":"","sources":["../../src/utils/billingDetails.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAoB,EACpB,KAAmB;;IAEnB,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,QAAQ,GAAG,KAAK,KAAK,QAAQ,CAAC;IAEpC,MAAM,KAAK,GAAG,MAAA,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;;QAC7B,MAAM,IAAI,GAAG,MAAA,MAAA,CAAC,CAAC,cAAc,CAAC,MAAM,0CAAE,WAAW,EAAE,mCAAI,EAAE,CAAC;QAC1D,OAAO,QAAQ;YACb,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAClD,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC,mCAAI,KAAK,CAAC,CAAC,CAAC,CAAC;IAEf,OAAO;QACL,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE;KAC3E,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,KAAmB;IAEnB,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,OAAO,SAAS,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;AAC5F,CAAC","sourcesContent":["import type { BillingPlan, BillingCycle } from '../types';\n\nexport interface PlanPrice {\n uid: string,\n stripePriceId: string,\n amount: string,\n currency: string,\n label: string, // e.g. \"$11 USD/month\"\n}\n\n/**\n * getPlanPrice — returns the price for the selected billing cycle.\n *\n * Matches plans by looking for \"annual\"/\"year\" or \"month\" in omName.\n * Falls back to the first plan if no match is found.\n *\n * @example\n * getPlanPrice(billingPlans, 'monthly')\n * // → { uid: '...', amount: '11', currency: 'USD', label: '$11 USD/month' }\n *\n * getPlanPrice(billingPlans, 'annual')\n * // → { uid: '...', amount: '111', currency: 'USD', label: '$111 USD/year' }\n */\nexport function getPlanPrice(\n plans: BillingPlan[],\n cycle: BillingCycle,\n): PlanPrice | null {\n if (!plans.length) return null;\n\n const isAnnual = cycle === 'annual';\n\n const match = plans.find((p) => {\n const name = p.ownershipModel.omName?.toLowerCase() ?? '';\n return isAnnual\n ? name.includes('annual') || name.includes('year')\n : name.includes('month');\n }) ?? plans[0];\n\n return {\n uid: match.uid,\n stripePriceId: match.stripePriceId,\n amount: match.amount,\n currency: match.currency,\n label: `$${match.amount} ${match.currency}/${isAnnual ? 'year' : 'month'}`,\n };\n}\n\n/**\n * getRenewalLabel — returns a \"Then $X/month\" style label for CartSummary.\n *\n * @example\n * getRenewalLabel(billingPlans, 'monthly') // → \"Then $11 USD/month\"\n * getRenewalLabel(billingPlans, 'annual') // → \"Then $111 USD/year\"\n */\nexport function getRenewalLabel(\n plans: BillingPlan[],\n cycle: BillingCycle,\n): string {\n const price = getPlanPrice(plans, cycle);\n if (!price) return '';\n return `Then $${price.amount} ${price.currency}/${cycle === 'annual' ? 'year' : 'month'}`;\n}\n"]}
|