@edx/frontend-app-alacarte-checkout 1.0.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 (166) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +2 -0
  3. package/README.rst +235 -0
  4. package/dist/Main.d.ts +3 -0
  5. package/dist/Main.js +15 -0
  6. package/dist/Main.js.map +1 -0
  7. package/dist/app.d.ts +3 -0
  8. package/dist/app.js +12 -0
  9. package/dist/app.js.map +1 -0
  10. package/dist/checkoutPage/CheckoutPage.d.ts +3 -0
  11. package/dist/checkoutPage/CheckoutPage.js +26 -0
  12. package/dist/checkoutPage/CheckoutPage.js.map +1 -0
  13. package/dist/components/BrandMarks/BrandMarks.d.ts +7 -0
  14. package/dist/components/BrandMarks/BrandMarks.js +9 -0
  15. package/dist/components/BrandMarks/BrandMarks.js.map +1 -0
  16. package/dist/components/CartSummary/CartSummary.d.ts +26 -0
  17. package/dist/components/CartSummary/CartSummary.js +41 -0
  18. package/dist/components/CartSummary/CartSummary.js.map +1 -0
  19. package/dist/components/CartSummary/Messages.d.ts +103 -0
  20. package/dist/components/CartSummary/Messages.js +106 -0
  21. package/dist/components/CartSummary/Messages.js.map +1 -0
  22. package/dist/components/Checkout/Checkout.d.ts +9 -0
  23. package/dist/components/Checkout/Checkout.js +82 -0
  24. package/dist/components/Checkout/Checkout.js.map +1 -0
  25. package/dist/components/Checkout/Messages.d.ts +8 -0
  26. package/dist/components/Checkout/Messages.js +10 -0
  27. package/dist/components/Checkout/Messages.js.map +1 -0
  28. package/dist/components/CheckoutGate/CheckoutGate.d.ts +9 -0
  29. package/dist/components/CheckoutGate/CheckoutGate.js +40 -0
  30. package/dist/components/CheckoutGate/CheckoutGate.js.map +1 -0
  31. package/dist/components/CheckoutGate/Messages.d.ts +8 -0
  32. package/dist/components/CheckoutGate/Messages.js +10 -0
  33. package/dist/components/CheckoutGate/Messages.js.map +1 -0
  34. package/dist/components/CountryUnavailableModal/CountryUnavailableModal.d.ts +9 -0
  35. package/dist/components/CountryUnavailableModal/CountryUnavailableModal.js +10 -0
  36. package/dist/components/CountryUnavailableModal/CountryUnavailableModal.js.map +1 -0
  37. package/dist/components/CountryUnavailableModal/Messages.d.ts +18 -0
  38. package/dist/components/CountryUnavailableModal/Messages.js +20 -0
  39. package/dist/components/CountryUnavailableModal/Messages.js.map +1 -0
  40. package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.d.ts +9 -0
  41. package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.js +31 -0
  42. package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.js.map +1 -0
  43. package/dist/components/CourseEnrollmentCard/Messages.d.ts +33 -0
  44. package/dist/components/CourseEnrollmentCard/Messages.js +35 -0
  45. package/dist/components/CourseEnrollmentCard/Messages.js.map +1 -0
  46. package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.d.ts +13 -0
  47. package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.js +11 -0
  48. package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.js.map +1 -0
  49. package/dist/components/EnrollmentSummary/EnrollmentSummary.d.ts +22 -0
  50. package/dist/components/EnrollmentSummary/EnrollmentSummary.js +33 -0
  51. package/dist/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -0
  52. package/dist/components/EnrollmentSummary/Messages.d.ts +48 -0
  53. package/dist/components/EnrollmentSummary/Messages.js +50 -0
  54. package/dist/components/EnrollmentSummary/Messages.js.map +1 -0
  55. package/dist/components/PaymentFields/PaymentFields.d.ts +9 -0
  56. package/dist/components/PaymentFields/PaymentFields.js +7 -0
  57. package/dist/components/PaymentFields/PaymentFields.js.map +1 -0
  58. package/dist/components/PaymentForm/Messages.d.ts +118 -0
  59. package/dist/components/PaymentForm/Messages.js +125 -0
  60. package/dist/components/PaymentForm/Messages.js.map +1 -0
  61. package/dist/components/PaymentForm/PaymentForm.d.ts +28 -0
  62. package/dist/components/PaymentForm/PaymentForm.js +121 -0
  63. package/dist/components/PaymentForm/PaymentForm.js.map +1 -0
  64. package/dist/components/StatusScreens/Messages.d.ts +108 -0
  65. package/dist/components/StatusScreens/Messages.js +114 -0
  66. package/dist/components/StatusScreens/Messages.js.map +1 -0
  67. package/dist/components/StatusScreens/StatusScreens.d.ts +24 -0
  68. package/dist/components/StatusScreens/StatusScreens.js +32 -0
  69. package/dist/components/StatusScreens/StatusScreens.js.map +1 -0
  70. package/dist/constants.d.ts +59 -0
  71. package/dist/constants.js +282 -0
  72. package/dist/constants.js.map +1 -0
  73. package/dist/data/hooks/keys.d.ts +14 -0
  74. package/dist/data/hooks/keys.js +15 -0
  75. package/dist/data/hooks/keys.js.map +1 -0
  76. package/dist/data/hooks/useCheckout.d.ts +61 -0
  77. package/dist/data/hooks/useCheckout.js +343 -0
  78. package/dist/data/hooks/useCheckout.js.map +1 -0
  79. package/dist/data/hooks/useCheckoutGate.d.ts +16 -0
  80. package/dist/data/hooks/useCheckoutGate.js +61 -0
  81. package/dist/data/hooks/useCheckoutGate.js.map +1 -0
  82. package/dist/data/hooks/useCheckoutMutations.d.ts +22 -0
  83. package/dist/data/hooks/useCheckoutMutations.js +166 -0
  84. package/dist/data/hooks/useCheckoutMutations.js.map +1 -0
  85. package/dist/data/hooks/useCheckoutQueries.d.ts +32 -0
  86. package/dist/data/hooks/useCheckoutQueries.js +107 -0
  87. package/dist/data/hooks/useCheckoutQueries.js.map +1 -0
  88. package/dist/data/services/subs/urls.d.ts +2 -0
  89. package/dist/data/services/subs/urls.js +4 -0
  90. package/dist/data/services/subs/urls.js.map +1 -0
  91. package/dist/i18n/index.d.ts +1 -0
  92. package/dist/i18n/index.js +2 -0
  93. package/dist/i18n/index.js.map +1 -0
  94. package/dist/index.d.ts +2 -0
  95. package/dist/index.js +3 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/messages.d.ts +8 -0
  98. package/dist/messages.js +10 -0
  99. package/dist/messages.js.map +1 -0
  100. package/dist/routes.d.ts +18 -0
  101. package/dist/routes.js +47 -0
  102. package/dist/routes.js.map +1 -0
  103. package/dist/slots/ExampleSlot/ExampleSlot.d.ts +3 -0
  104. package/dist/slots/ExampleSlot/ExampleSlot.js +6 -0
  105. package/dist/slots/ExampleSlot/ExampleSlot.js.map +1 -0
  106. package/dist/slots.d.ts +3 -0
  107. package/dist/slots.js +3 -0
  108. package/dist/slots.js.map +1 -0
  109. package/dist/style.scss +14 -0
  110. package/dist/styles/components/brandMarks.scss +41 -0
  111. package/dist/styles/components/cartSummary.scss +29 -0
  112. package/dist/styles/components/checkout.scss +29 -0
  113. package/dist/styles/components/checkoutStepper.scss +9 -0
  114. package/dist/styles/components/courseEnrollmentCard.scss +38 -0
  115. package/dist/styles/components/edxUnlimitedBanner.scss +138 -0
  116. package/dist/styles/components/enrollmentSummary.scss +19 -0
  117. package/dist/styles/components/paymentFields.scss +15 -0
  118. package/dist/styles/components/paymentForm.scss +139 -0
  119. package/dist/styles/components/statusScreens.scss +39 -0
  120. package/dist/styles/components/successScreen.scss +0 -0
  121. package/dist/types.d.ts +232 -0
  122. package/dist/types.js +2 -0
  123. package/dist/types.js.map +1 -0
  124. package/dist/utils/auth.d.ts +7 -0
  125. package/dist/utils/auth.js +28 -0
  126. package/dist/utils/auth.js.map +1 -0
  127. package/dist/utils/billingDetails.d.ts +30 -0
  128. package/dist/utils/billingDetails.js +48 -0
  129. package/dist/utils/billingDetails.js.map +1 -0
  130. package/dist/utils/bootstrapApi.d.ts +20 -0
  131. package/dist/utils/bootstrapApi.js +142 -0
  132. package/dist/utils/bootstrapApi.js.map +1 -0
  133. package/dist/utils/cartApi.d.ts +16 -0
  134. package/dist/utils/cartApi.js +133 -0
  135. package/dist/utils/cartApi.js.map +1 -0
  136. package/dist/utils/checkout.d.ts +6 -0
  137. package/dist/utils/checkout.js +28 -0
  138. package/dist/utils/checkout.js.map +1 -0
  139. package/dist/utils/checkoutErrors.d.ts +1 -0
  140. package/dist/utils/checkoutErrors.js +11 -0
  141. package/dist/utils/checkoutErrors.js.map +1 -0
  142. package/dist/utils/fetchActiveSubscriptions.d.ts +4 -0
  143. package/dist/utils/fetchActiveSubscriptions.js +36 -0
  144. package/dist/utils/fetchActiveSubscriptions.js.map +1 -0
  145. package/dist/utils/fetchBillingPlans.d.ts +2 -0
  146. package/dist/utils/fetchBillingPlans.js +48 -0
  147. package/dist/utils/fetchBillingPlans.js.map +1 -0
  148. package/dist/utils/graphqlClient.d.ts +16 -0
  149. package/dist/utils/graphqlClient.js +39 -0
  150. package/dist/utils/graphqlClient.js.map +1 -0
  151. package/dist/utils/index.d.ts +3 -0
  152. package/dist/utils/index.js +17 -0
  153. package/dist/utils/index.js.map +1 -0
  154. package/dist/utils/planQuery.d.ts +16 -0
  155. package/dist/utils/planQuery.js +57 -0
  156. package/dist/utils/planQuery.js.map +1 -0
  157. package/dist/utils/provision.d.ts +48 -0
  158. package/dist/utils/provision.js +161 -0
  159. package/dist/utils/provision.js.map +1 -0
  160. package/dist/utils/queries.d.ts +35 -0
  161. package/dist/utils/queries.js +168 -0
  162. package/dist/utils/queries.js.map +1 -0
  163. package/dist/utils/stripe.d.ts +23 -0
  164. package/dist/utils/stripe.js +86 -0
  165. package/dist/utils/stripe.js.map +1 -0
  166. package/package.json +85 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * api/queries.ts — GraphQL documents for the checkout flow.
3
+ * Kept as plain strings so they can be reused, logged, or tested in isolation.
4
+ *
5
+ * Mutation reference (from API doc):
6
+ * updateTwouCustomer — toggle Stripe test clock on/off
7
+ * createTwouCart — user proceeds to payment
8
+ * updateTwouCartItem — attach billing plan / payment method to cart
9
+ * updateTwouCart — persist auto-renewal consent (SUBS-228)
10
+ * startBusinessProcessableTransitionV2 — place order
11
+ * updateTwouCartItem — retry: attach new payment method
12
+ */
13
+ // ─── Mutations ────────────────────────────────────────────────────────────────
14
+ /** Creates a new cart during checkout. */
15
+ export const CREATE_CART = `mutation CreateCart($input: TwouCartInput!) {
16
+ createTwouCart(input: $input) { biId uid }
17
+ }`;
18
+ /** Updates a cart item — attach billing plan / payment method. */
19
+ export const UPDATE_CART_ITEM = `mutation UpdateCartItem($input: TwouCartItemInput!) {
20
+ updateTwouCartItem(input: $input) { uid }
21
+ }`;
22
+ /** Persists auto-renewal consent fields on the TwouCart BI (SUBS-228). */
23
+ export const UPDATE_CART = `mutation UpdateCart($input: TwouCartInput!) {
24
+ updateTwouCart(input: $input) { uid }
25
+ }`;
26
+ /** Triggers the backend workflow to place the order. */
27
+ export const PLACE_ORDER = `mutation PlaceOrder($input: StartBusinessProcessableTransitionInputV2!) {
28
+ startBusinessProcessableTransitionV2(input: $input) { uid bpStatus { code } }
29
+ }`;
30
+ // ─── Queries ──────────────────────────────────────────────────────────────────
31
+ export const GET_CART_ITEM = `query GetCartItem($biId: String!) {
32
+ getBusinessInteraction(biId: $biId) {
33
+ uid
34
+ hiConsistsOf { ... on TwouCartItem { uid } }
35
+ }
36
+ }`;
37
+ export const GET_CART_STATUS = `query GetCartStatus($biId: String!) {
38
+ getBusinessInteraction(biId: $biId) {
39
+ bpStatus { code }
40
+ hiConsistsOf { ... on TwouCartItem { tStripeSubscriptionId } }
41
+ ... on TwouCart {
42
+ tStripeConfirmationRedirectUrl
43
+ tPaypalApprovalUrl
44
+ cmComments { edges { node { comText } } }
45
+ }
46
+ }
47
+ }`;
48
+ export const FIND_SUBSCRIPTION = `query FindSubscription($filter: String!) {
49
+ filterTwouSubscription(_first: 1, _filter: $filter) {
50
+ edges {
51
+ node {
52
+ biId
53
+ tRenewalType
54
+ hiConsistsOf {
55
+ ... on TwouSubscriptionItem {
56
+ paybBillingPlan {
57
+ tStripePriceId
58
+ bipOwnershipModel { omName }
59
+ }
60
+ paybPayment {
61
+ ... on TwouStripePayment {
62
+ tStripePaymentMethodId
63
+ tPaymentMethodLast4
64
+ tPaymentMethodBrand
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }`;
73
+ /** Fetch the viax customer uid by email using filterTwouCustomer. */
74
+ export const GET_CUSTOMER = `query GetCustomer($filter: String!) {
75
+ filterTwouCustomer(_first: 1, _filter: $filter) {
76
+ edges {
77
+ node {
78
+ uid
79
+ parId
80
+ parParty {
81
+ paName
82
+ paContacts {
83
+ edges {
84
+ node {
85
+ ... on EmailAddressContactMedium {
86
+ email
87
+ }
88
+ }
89
+ }
90
+ }
91
+ ... on Individual {
92
+ indFirstName
93
+ indLastName
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }`;
100
+ /** Fetch the TwouSubscriptionPlan uid to use as biiSalable in createTwouCart. */
101
+ export const GET_PRODUCT = `query GetProduct($maId: String!) {
102
+ getProduct(maId: $maId) {
103
+ maName
104
+ uid
105
+ ... on TwouSubscriptionPlan {
106
+ saOwnershipModels {
107
+ uid
108
+ omName
109
+ }
110
+ }
111
+ }
112
+ }`;
113
+ export const GET_BILLING_PLANS = `query GetBillingPlans {
114
+ filterCustomerBillingPlan(_first: 10) {
115
+ edges {
116
+ node {
117
+ uid
118
+ tStripePriceId
119
+ tStripeAmount {
120
+ amount
121
+ units { code }
122
+ }
123
+ bipIsTemplate
124
+ bipOwnershipModel { uid omName }
125
+ }
126
+ }
127
+ }
128
+ }`;
129
+ /** Fetch the number of active subscriptions — unauthenticated. */
130
+ export const GET_ACTIVE_SUBSCRIPTIONS_STATE = `query activeSubscriptions {
131
+ tActiveSubscriptionsState
132
+ }`;
133
+ /** Check whether the currently authenticated user already has an active subscription.
134
+ * tIsCurrentUserSubscriber is a JSON scalar — returns the full object, no subfield selection allowed. */
135
+ export const GET_IS_CURRENT_USER_SUBSCRIBER = `query IsCurrentUserSubscriber {
136
+ tIsCurrentUserSubscriber
137
+ }`;
138
+ /** Fetch a course for the enrollment card when ?course_run_key= is present. */
139
+ export const GET_COURSE_DETAIL = `query CourseDetail($filter: String!) {
140
+ filterTwouCourse(_first: 1, _filter: $filter) {
141
+ edges {
142
+ node {
143
+ maId
144
+ maName
145
+ maDescription
146
+ tShortDescription
147
+ imageUrl
148
+ tCourseKey
149
+ tBrandText
150
+ tPrimarySubjectArea
151
+ tLevelType
152
+ tLanguage
153
+ tPacingType { code }
154
+ tDurationLow
155
+ tDurationHigh
156
+ tDurationUnit { code }
157
+ tEffortLow
158
+ tEffortHigh
159
+ tEffortUnit { code }
160
+ tUrlCourse
161
+ tB2cSubscriptionInclusion
162
+ tHasOfacRestrictions
163
+ tCatalogActive
164
+ }
165
+ }
166
+ }
167
+ }`;
168
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/utils/queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iFAAiF;AAEjF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG;;EAE9B,CAAC;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,wDAAwD;AACxD,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,iFAAiF;AAEjF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;EAK3B,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;EAU7B,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;EAwB/B,CAAC;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;EAyB1B,CAAC;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;EAWzB,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;EAe/B,CAAC;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,8BAA8B,GAAG;;EAE5C,CAAC;AAEH;0GAC0G;AAC1G,MAAM,CAAC,MAAM,8BAA8B,GAAG;;EAE5C,CAAC;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4B/B,CAAC","sourcesContent":["/**\n * api/queries.ts — GraphQL documents for the checkout flow.\n * Kept as plain strings so they can be reused, logged, or tested in isolation.\n *\n * Mutation reference (from API doc):\n * updateTwouCustomer — toggle Stripe test clock on/off\n * createTwouCart — user proceeds to payment\n * updateTwouCartItem — attach billing plan / payment method to cart\n * updateTwouCart — persist auto-renewal consent (SUBS-228)\n * startBusinessProcessableTransitionV2 — place order\n * updateTwouCartItem — retry: attach new payment method\n */\n\n// ─── Mutations ────────────────────────────────────────────────────────────────\n\n/** Creates a new cart during checkout. */\nexport const CREATE_CART = `mutation CreateCart($input: TwouCartInput!) {\n createTwouCart(input: $input) { biId uid }\n}`;\n\n/** Updates a cart item — attach billing plan / payment method. */\nexport const UPDATE_CART_ITEM = `mutation UpdateCartItem($input: TwouCartItemInput!) {\n updateTwouCartItem(input: $input) { uid }\n}`;\n\n/** Persists auto-renewal consent fields on the TwouCart BI (SUBS-228). */\nexport const UPDATE_CART = `mutation UpdateCart($input: TwouCartInput!) {\n updateTwouCart(input: $input) { uid }\n}`;\n\n/** Triggers the backend workflow to place the order. */\nexport const PLACE_ORDER = `mutation PlaceOrder($input: StartBusinessProcessableTransitionInputV2!) {\n startBusinessProcessableTransitionV2(input: $input) { uid bpStatus { code } }\n}`;\n\n// ─── Queries ──────────────────────────────────────────────────────────────────\n\nexport const GET_CART_ITEM = `query GetCartItem($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n uid\n hiConsistsOf { ... on TwouCartItem { uid } }\n }\n}`;\n\nexport const GET_CART_STATUS = `query GetCartStatus($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n bpStatus { code }\n hiConsistsOf { ... on TwouCartItem { tStripeSubscriptionId } }\n ... on TwouCart {\n tStripeConfirmationRedirectUrl\n tPaypalApprovalUrl\n cmComments { edges { node { comText } } }\n }\n }\n}`;\n\nexport const FIND_SUBSCRIPTION = `query FindSubscription($filter: String!) {\n filterTwouSubscription(_first: 1, _filter: $filter) {\n edges {\n node {\n biId\n tRenewalType\n hiConsistsOf {\n ... on TwouSubscriptionItem {\n paybBillingPlan {\n tStripePriceId\n bipOwnershipModel { omName }\n }\n paybPayment {\n ... on TwouStripePayment {\n tStripePaymentMethodId\n tPaymentMethodLast4\n tPaymentMethodBrand\n }\n }\n }\n }\n }\n }\n }\n}`;\n\n/** Fetch the viax customer uid by email using filterTwouCustomer. */\nexport const GET_CUSTOMER = `query GetCustomer($filter: String!) {\n filterTwouCustomer(_first: 1, _filter: $filter) {\n edges {\n node {\n uid\n parId\n parParty {\n paName\n paContacts {\n edges {\n node {\n ... on EmailAddressContactMedium {\n email\n }\n }\n }\n }\n ... on Individual {\n indFirstName\n indLastName\n }\n }\n }\n }\n }\n}`;\n\n/** Fetch the TwouSubscriptionPlan uid to use as biiSalable in createTwouCart. */\nexport const GET_PRODUCT = `query GetProduct($maId: String!) {\n getProduct(maId: $maId) {\n maName\n uid\n ... on TwouSubscriptionPlan {\n saOwnershipModels {\n uid\n omName\n }\n }\n }\n}`;\n\nexport const GET_BILLING_PLANS = `query GetBillingPlans {\n filterCustomerBillingPlan(_first: 10) {\n edges {\n node {\n uid\n tStripePriceId\n tStripeAmount {\n amount\n units { code }\n }\n bipIsTemplate\n bipOwnershipModel { uid omName }\n }\n }\n }\n}`;\n\n/** Fetch the number of active subscriptions — unauthenticated. */\nexport const GET_ACTIVE_SUBSCRIPTIONS_STATE = `query activeSubscriptions {\n tActiveSubscriptionsState\n}`;\n\n/** Check whether the currently authenticated user already has an active subscription.\n * tIsCurrentUserSubscriber is a JSON scalar — returns the full object, no subfield selection allowed. */\nexport const GET_IS_CURRENT_USER_SUBSCRIBER = `query IsCurrentUserSubscriber {\n tIsCurrentUserSubscriber\n}`;\n\n/** Fetch a course for the enrollment card when ?course_run_key= is present. */\nexport const GET_COURSE_DETAIL = `query CourseDetail($filter: String!) {\n filterTwouCourse(_first: 1, _filter: $filter) {\n edges {\n node {\n maId\n maName\n maDescription\n tShortDescription\n imageUrl\n tCourseKey\n tBrandText\n tPrimarySubjectArea\n tLevelType\n tLanguage\n tPacingType { code }\n tDurationLow\n tDurationHigh\n tDurationUnit { code }\n tEffortLow\n tEffortHigh\n tEffortUnit { code }\n tUrlCourse\n tB2cSubscriptionInclusion\n tHasOfacRestrictions\n tCatalogActive\n }\n }\n }\n}`;\n"]}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * api/stripe.ts — load Stripe.js and return a Stripe instance.
3
+ * Injects the script tag if needed. In an MFE you may prefer
4
+ * `@stripe/stripe-js`'s loadStripe(); swap this out if so.
5
+ */
6
+ import type { StripeLike } from '../types';
7
+ export declare function loadStripe(publishableKey: string): Promise<StripeLike>;
8
+ /** Query parameter carrying the cart biId on Stripe's 3DS return URL. */
9
+ export declare const RETURN_CART_PARAM = "cart_bi_id";
10
+ export declare function getReturnedCartBiIdFromSearch(search: string): string | null;
11
+ export declare function getReturnedCartBiIdFromUrl(): string | null;
12
+ /**
13
+ * Set ?cart_bi_id= while preserving other params.
14
+ * Avoid URLSearchParams — it treats bare `+` as a space and would break
15
+ * course_run_key values like HarvardX+CS50+X.
16
+ */
17
+ export declare function withReturnCartQuery(search: string, cartBiId: string): string;
18
+ /**
19
+ * Absolute URL for tStripeReturnUrl — Stripe rejects relative return URLs.
20
+ * Appends the cart biId so checkout can poll that attempt after 3DS.
21
+ * Keeps the learner's existing params (plan, course_run_key).
22
+ */
23
+ export declare function buildStripeReturnUrl(cartBiId: string): string | null;
@@ -0,0 +1,86 @@
1
+ let stripeLoadingPromise = null;
2
+ export function loadStripe(publishableKey) {
3
+ if (typeof window === 'undefined') {
4
+ return Promise.reject(new Error('Stripe.js requires a browser environment'));
5
+ }
6
+ if (window.Stripe)
7
+ return Promise.resolve(window.Stripe(publishableKey));
8
+ if (!stripeLoadingPromise) {
9
+ stripeLoadingPromise = new Promise((resolve, reject) => {
10
+ const fail = (err) => {
11
+ stripeLoadingPromise = null;
12
+ reject(err);
13
+ };
14
+ const done = () => {
15
+ if (window.Stripe)
16
+ resolve();
17
+ else
18
+ fail(new Error('Stripe.js loaded but window.Stripe is missing'));
19
+ };
20
+ const existing = document.querySelector('script[src^="https://js.stripe.com/v3"]');
21
+ if (existing) {
22
+ if (window.Stripe) {
23
+ resolve();
24
+ return;
25
+ }
26
+ existing.addEventListener('load', done, { once: true });
27
+ existing.addEventListener('error', () => fail(new Error('Failed to load Stripe.js')), { once: true });
28
+ // Race: load may have completed between querySelector and addEventListener.
29
+ if (window.Stripe)
30
+ resolve();
31
+ return;
32
+ }
33
+ const s = document.createElement('script');
34
+ s.src = 'https://js.stripe.com/v3/';
35
+ s.async = true;
36
+ s.onload = done;
37
+ s.onerror = () => fail(new Error('Failed to load Stripe.js'));
38
+ document.head.appendChild(s);
39
+ });
40
+ }
41
+ return stripeLoadingPromise.then(() => window.Stripe(publishableKey));
42
+ }
43
+ /** Query parameter carrying the cart biId on Stripe's 3DS return URL. */
44
+ export const RETURN_CART_PARAM = 'cart_bi_id';
45
+ export function getReturnedCartBiIdFromSearch(search) {
46
+ const match = new RegExp(`[?&]${RETURN_CART_PARAM}=([^&]*)`, 'i').exec(search);
47
+ if (!match)
48
+ return null;
49
+ try {
50
+ return decodeURIComponent(match[1]).trim() || null;
51
+ }
52
+ catch (_a) {
53
+ return match[1].trim() || null;
54
+ }
55
+ }
56
+ export function getReturnedCartBiIdFromUrl() {
57
+ if (typeof window === 'undefined')
58
+ return null;
59
+ return getReturnedCartBiIdFromSearch(window.location.search);
60
+ }
61
+ /**
62
+ * Set ?cart_bi_id= while preserving other params.
63
+ * Avoid URLSearchParams — it treats bare `+` as a space and would break
64
+ * course_run_key values like HarvardX+CS50+X.
65
+ */
66
+ export function withReturnCartQuery(search, cartBiId) {
67
+ const raw = search.startsWith('?') ? search.slice(1) : search;
68
+ const parts = raw
69
+ .split('&')
70
+ .filter((part) => (part.length > 0
71
+ && !part.toLowerCase().startsWith(`${RETURN_CART_PARAM}=`)));
72
+ parts.push(`${RETURN_CART_PARAM}=${encodeURIComponent(cartBiId)}`);
73
+ return `?${parts.join('&')}`;
74
+ }
75
+ /**
76
+ * Absolute URL for tStripeReturnUrl — Stripe rejects relative return URLs.
77
+ * Appends the cart biId so checkout can poll that attempt after 3DS.
78
+ * Keeps the learner's existing params (plan, course_run_key).
79
+ */
80
+ export function buildStripeReturnUrl(cartBiId) {
81
+ if (typeof window === 'undefined')
82
+ return null;
83
+ const { origin, pathname, search } = window.location;
84
+ return `${origin}${pathname}${withReturnCartQuery(search, cartBiId)}`;
85
+ }
86
+ //# sourceMappingURL=stripe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../src/utils/stripe.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api/stripe.ts — load Stripe.js and return a Stripe instance.\n * Injects the script tag if needed. In an MFE you may prefer\n * `@stripe/stripe-js`'s loadStripe(); swap this out if so.\n */\nimport type { StripeLike } from '../types';\n\nlet stripeLoadingPromise: Promise<void> | null = null;\n\nexport function loadStripe(publishableKey: string): Promise<StripeLike> {\n if (typeof window === 'undefined') {\n return Promise.reject(new Error('Stripe.js requires a browser environment'));\n }\n if (window.Stripe) return Promise.resolve(window.Stripe(publishableKey));\n if (!stripeLoadingPromise) {\n stripeLoadingPromise = new Promise<void>((resolve, reject) => {\n const fail = (err: Error) => {\n stripeLoadingPromise = null;\n reject(err);\n };\n\n const done = () => {\n if (window.Stripe) resolve();\n else fail(new Error('Stripe.js loaded but window.Stripe is missing'));\n };\n\n const existing = document.querySelector('script[src^=\"https://js.stripe.com/v3\"]');\n if (existing) {\n if (window.Stripe) {\n resolve();\n return;\n }\n existing.addEventListener('load', done, { once: true });\n existing.addEventListener('error', () => fail(new Error('Failed to load Stripe.js')), { once: true });\n // Race: load may have completed between querySelector and addEventListener.\n if (window.Stripe) resolve();\n return;\n }\n\n const s = document.createElement('script');\n s.src = 'https://js.stripe.com/v3/';\n s.async = true;\n s.onload = done;\n s.onerror = () => fail(new Error('Failed to load Stripe.js'));\n document.head.appendChild(s);\n });\n }\n return stripeLoadingPromise.then(() => window.Stripe!(publishableKey));\n}\n\n/** Query parameter carrying the cart biId on Stripe's 3DS return URL. */\nexport const RETURN_CART_PARAM = 'cart_bi_id';\n\nexport function getReturnedCartBiIdFromSearch(search: string): string | null {\n const match = new RegExp(`[?&]${RETURN_CART_PARAM}=([^&]*)`, 'i').exec(search);\n if (!match) return null;\n try {\n return decodeURIComponent(match[1]).trim() || null;\n } catch {\n return match[1].trim() || null;\n }\n}\n\nexport function getReturnedCartBiIdFromUrl(): string | null {\n if (typeof window === 'undefined') return null;\n return getReturnedCartBiIdFromSearch(window.location.search);\n}\n\n/**\n * Set ?cart_bi_id= while preserving other params.\n * Avoid URLSearchParams — it treats bare `+` as a space and would break\n * course_run_key values like HarvardX+CS50+X.\n */\nexport function withReturnCartQuery(search: string, cartBiId: string): string {\n const raw = search.startsWith('?') ? search.slice(1) : search;\n const parts = raw\n .split('&')\n .filter((part) => (\n part.length > 0\n && !part.toLowerCase().startsWith(`${RETURN_CART_PARAM}=`)\n ));\n parts.push(`${RETURN_CART_PARAM}=${encodeURIComponent(cartBiId)}`);\n return `?${parts.join('&')}`;\n}\n\n/**\n * Absolute URL for tStripeReturnUrl — Stripe rejects relative return URLs.\n * Appends the cart biId so checkout can poll that attempt after 3DS.\n * Keeps the learner's existing params (plan, course_run_key).\n */\nexport function buildStripeReturnUrl(cartBiId: string): string | null {\n if (typeof window === 'undefined') return null;\n const { origin, pathname, search } = window.location;\n return `${origin}${pathname}${withReturnCartQuery(search, cartBiId)}`;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@edx/frontend-app-alacarte-checkout",
3
+ "version": "1.0.0",
4
+ "description": "alacarte Checkout Frontend application",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/edx/frontend-app-alacarte-checkout.git"
8
+ },
9
+ "exports": {
10
+ ".": "./dist/index.js"
11
+ },
12
+ "files": [
13
+ "/dist"
14
+ ],
15
+ "workspaces": [
16
+ "packages/*"
17
+ ],
18
+ "browserslist": [
19
+ "extends @edx/browserslist-config"
20
+ ],
21
+ "sideEffects": [
22
+ "*.css",
23
+ "*.scss"
24
+ ],
25
+ "atlasTranslations": {
26
+ "path": "translations/frontend-app-subscription-checkout/src/i18n/messages",
27
+ "dependencies": [
28
+ "@openedx/frontend-base"
29
+ ]
30
+ },
31
+ "scripts": {
32
+ "build": "make build",
33
+ "build:ci": "make build-ci",
34
+ "build:packages": "make build-packages",
35
+ "clean": "make clean",
36
+ "clean:packages": "make clean-packages",
37
+ "dev": "PORT=1996 PUBLIC_PATH=/checkout openedx dev",
38
+ "dev:site": "make dev-site",
39
+ "dev:packages": "make dev-packages",
40
+ "i18n_extract": "openedx formatjs extract",
41
+ "lint": "openedx lint .",
42
+ "lint:fix": "openedx lint --fix .",
43
+ "prepack": "npm run clean && npm run build",
44
+ "snapshot": "openedx test --updateSnapshot",
45
+ "test": "openedx test --coverage --passWithNoTests",
46
+ "translations:pull": "openedx translations:pull",
47
+ "watch:build": "nodemon --exec 'npm run build'"
48
+ },
49
+ "author": "Open edX",
50
+ "license": "AGPL-3.0",
51
+ "homepage": "https://github.com/edx/frontend-app-subscription-checkout#readme",
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/edx/frontend-app-subscription-checkout/issues"
57
+ },
58
+ "dependencies": {
59
+ "@edx/openedx-atlas": "^0.7.0",
60
+ "@openedx/brand-openedx": "npm:@edx/elm-theme@^1.11.1",
61
+ "react-helmet": "^6.1.0"
62
+ },
63
+ "devDependencies": {
64
+ "@edx/browserslist-config": "^1.5.0",
65
+ "@testing-library/jest-dom": "^6.9.1",
66
+ "@testing-library/react": "^16.3.0",
67
+ "@testing-library/user-event": "^14.6.1",
68
+ "@types/jest": "^29.5.14",
69
+ "@types/react": "^18",
70
+ "@types/react-dom": "^18",
71
+ "jest": "^29",
72
+ "nodemon": "^3.1.14",
73
+ "tsc-alias": "^1.8.16",
74
+ "turbo": "^2.9.2"
75
+ },
76
+ "peerDependencies": {
77
+ "@openedx/frontend-base": "^1.0.0-alpha || 0.0.0-dev",
78
+ "@openedx/paragon": "^23",
79
+ "@tanstack/react-query": "^5",
80
+ "react": "^18",
81
+ "react-dom": "^18",
82
+ "react-router": "^6",
83
+ "react-router-dom": "^6"
84
+ }
85
+ }