diff options
| author | Joe Carstairs <65492573+joeacarstairs@users.noreply.github.com> | 2024-01-25 17:02:50 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-25 17:02:50 +0000 |
| commit | 64c5ee53a0d158b38caff3285dac008826d7ae88 (patch) | |
| tree | 7542f8c5b82b3327d82cc5defe2e84f86d6f11e7 /src/scripts/wc | |
| parent | 7140956da0820d4b818119eff7dfc72345f3328e (diff) | |
Update from staging (#70)
* 33 / Pay annual subscription by PayPal (#68)
* Refactors paypal-buttons -> paypal-product-buttons WebComponent
* Pulls out address to data file
* Defines annual subscription prices data hash
* Styles paypal-product-buttons
* Styles <address>
* Details have cursor:pointer on the summary
* Moves Jyne section to separate page
* Rewrites payment options using details components
* Reworks translation typing
* Defines MembershipType enum
* Adds translations for MembershipType enum
* Can pay annual subs by PayPal
* Styles tables better
* Replaces a few straggler --margins with --spacing (#67)
---------
Co-authored-by: Joe Carstairs <118172583+jcarstairs-scottlogic@users.noreply.github.com>
Diffstat (limited to 'src/scripts/wc')
| -rw-r--r-- | src/scripts/wc/paypal-product-buttons.ts | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/scripts/wc/paypal-product-buttons.ts b/src/scripts/wc/paypal-product-buttons.ts new file mode 100644 index 0000000..ca06524 --- /dev/null +++ b/src/scripts/wc/paypal-product-buttons.ts @@ -0,0 +1,146 @@ +import { type PayPalNamespace, loadScript } from '@paypal/paypal-js'; + +const backendUrl = '/api'; + +class PaypalProductButtons extends HTMLElement { + constructor() { + super(); + + try { + const paypalClientId = this.getAttribute('paypal-client-id'); + + const paypalOptions = { + clientId: paypalClientId, + locale: 'en_GB', + commit: false, + currency: 'GBP', + intent: 'capture', + }; + + loadScript(paypalOptions) + .catch((err) => { + this.innerHTML = ` + <details> + <summary>Failed to load PayPal buttons</summary> + <p>${err}</p> + </details> + `; + console.error(`Failed to load the PayPal JS SDK script. ${err}`); + }) + .then((paypal) => { + const constructButtons = paypal?.Buttons; + if (paypal && constructButtons) { + const successPageUrl = this.getAttribute('success-page-url'); + this.innerHTML = ''; + const div = document.createElement('div'); + this.appendChild(div); + this.makePaypalButtons(constructButtons, successPageUrl).render(div); + } + }) + .catch((err) => { + this.innerHTML = ` + <details> + <summary>Failed to load PayPal buttons</summary> + <p>${err}</p> + </details> + `; + console.error(`Failed to construct the PayPal buttons. ${err}`); + }); + } catch (err) { + this.innerHTML = ` + <details> + <summary>Failed to load PayPal buttons</summary> + <p>${err}</p> + </details> + `; + } + } + + makePaypalButtons( + constructButtons: Required<PayPalNamespace>['Buttons'], + successPageUrl: string + ) { + const productDescription = this.getAttribute('product-description'); + const shortDescription = this.getAttribute('short-description'); + const totalPrice = this.getAttribute('total-price'); + + return constructButtons({ + // This function is called to set up the transaction details without actually carrying it out + async createOrder() { + const body = JSON.stringify({ + productDescription, + shortDescription, + totalPrice, + }); + const headers = { + 'Content-Type': 'application/json', + }; + + let response: Response; + try { + response = await fetch(`${backendUrl}/order`, { method: 'POST', body, headers }); + } catch (err) { + throw new Error(`Failed to create order. ${err}`); + } + + if (!response.ok) { + throw new Error(` + Failed to create order. Backend response was + ${response.status} ${response.statusText}. ${await response.text()} + `); + } + + const { orderId, approvalLink } = await response.json(); + console.info(` + Successfully created order with ID + ${orderId}. Approval link: ${approvalLink} + `); + return orderId; + }, + + // This function is called after payment is confirmed + async onApprove(data) { + const body = JSON.stringify({ + id: data.orderID, + isApproved: true, + }); + const headers = { + 'Content-Type': 'application/json', + }; + + let response: Response; + try { + response = await fetch(`${backendUrl}/order`, { method: 'PATCH', body, headers }); + } catch (err) { + throw new Error(`Failed to capture order. ${err}`); + } + + if (!response.ok) { + throw new Error(` + Failed to capture order. Backend response was ${response.status} + ${response.statusText}. ${await response.text()} + `); + } + + const { orderId, requestStatus } = await response.json(); + console.info(` + Successfully captured order with ID ${orderId}, + request status is ${requestStatus}. + `); + + console.info(`Redirecting to confirmation page, ${successPageUrl}...`); + window.location.href = successPageUrl; + }, + }); + } + + getAttribute(key: string): string { + const value = super.getAttribute(key); + if (!value || typeof value !== 'string') { + throw new Error(`${key} attribute not provided to PaypalProductButtons component.`); + } + return value; + } +} + +customElements.define('paypal-product-buttons', PaypalProductButtons); |
