--- title: Sessions and events description: >- Learn how to create payment request sessions and handle events in Shop Pay Wallet API. api_name: commerce-components source_url: html: 'https://shopify.dev/docs/api/commerce-components/pay/sessions-and-events' md: 'https://shopify.dev/docs/api/commerce-components/pay/sessions-and-events.md' --- # Sessions and events **Note:** This API is **only available to select merchants and partners** using third-party checkout solutions. For more information, contact [our enterprise sales team](https://www.shopify.com/solutions/shop-pay/enterprise#contact-sales). Learn how to create payment request sessions, attach event listeners, and handle the payment lifecycle with the Shop Pay Wallet API. *** ## Configure the API Use the `.myshopify.com` hostname from your onboarding email to configure the API. ```javascript window.ShopPay.PaymentRequest.configure({ shopId: 1, clientId: "[REPLACE-ME.myshopify.com]", }) ``` *** ## Create a session Create a session to make a payment request. `shop_id` (`integer`) can be retrieved from the `shop` object in the [Admin API](https://shopify.dev/docs/api/admin-graphql/latest/queries/shop). [PaymentRequest fields are defined here.](https://shopify.dev/docs/api/commerce-components/pay/frontend-api-reference#shoppaypaymentrequest) ```javascript const initialPaymentRequest = window.ShopPay.PaymentRequest.build({ lineItems: [ { label: "T-Shirt", originalItemPrice: { amount: 10.00, currencyCode: "USD" }, itemDiscounts: [ { label: "10% off", amount: { amount: 1.00, currencyCode: "USD" } } ], finalItemPrice: { amount: 9.00, currencyCode: "USD" }, quantity: 2, sku: "t-shirt", requiresShipping: true, originalLinePrice: { amount: 20.00, currencyCode: "USD" }, lineDiscounts: [ { label: "10% off", amount: { amount: 2.00, currencyCode: "USD" } } ], finalLinePrice: { amount: 18.00, currencyCode: "USD" }, } ], discountCodes: [], deliveryMethods: [], subtotal: { amount: 18.00, currencyCode: "USD" }, totalTax: { amount: 1.25, currencyCode: "USD" }, total: { amount: 19.25, currencyCode: "USD" }, presentmentCurrency: "USD", locale: 'en', }); const session = window.ShopPay.PaymentRequest.createSession({ paymentRequest: initialPaymentRequest }); ``` *** ## Attach event listeners Use [`ShopPayPaymentRequestSessionCreate`](https://shopify.dev/docs/api/commerce-components/pay/graphql-pre-payment#example-shoppaypaymentrequestsessioncreate) on your server to create a session. ```javascript session.addEventListener("sessionrequested", (ev) => { // Shop Pay Payment Request Session on your server const response = fetch('/replace_with_your_endpoint', { method: 'POST', body: JSON.stringify({ payment_request: initialPaymentRequest }), headers: { 'Content-Type': 'application/json', }, }).then(response => response.json()).then(data => { const {token, checkoutUrl, sourceIdentifier} = data; // optionally update the payment request if it has changed since it was created const updatedPaymentRequest = window.ShopPay.PaymentRequest.build({YOUR_UPDATED_PAYMENT_REQUEST}); session.completeSessionRequest({token, checkoutUrl, sourceIdentifier, updatedPaymentRequest}); }); }); ``` Listen to events that may change calculations such as when a delivery method type, shipping address, delivery method, pickup location, pickup location filter, or discount code changes. Recalculate the payment request and update the session. ```javascript session.addEventListener("deliverymethodtypechanged", async (ev) => { const currentPaymentRequest = session.paymentRequest; const deliveryMethodType = ev.deliveryMethodType; let pickupLocations = []; if (deliveryMethodType === 'PICKUP') { pickupLocations = await fetchPickupLocations(); } // Update the payment request based on the delivery method type change const updatedPaymentRequest = window.ShopPay.PaymentRequest.build({ ...currentPaymentRequest, pickupLocations, }); session.completeDeliveryMethodTypeChange({ updatedPaymentRequest: updatedPaymentRequest }); }); ``` ```javascript session.addEventListener("shippingaddresschanged", async (ev) => { const currentPaymentRequest = session.paymentRequest; const selectedAddress = ev.shippingAddress; // Update the payment request based on the shipping address change const updatedPaymentRequest = window.ShopPay.PaymentRequest.build({ ...currentPaymentRequest, deliveryMethods: [ { label: "Standard", amount: { amount: 10.00, currencyCode: "USD" }, code: "STANDARD", minDeliveryDate: '2024-01-01', maxDeliveryDate: '2027-01-01', }, { label: "Express", amount: { amount: 20.00, currencyCode: "USD" }, code: "EXPRESS", minDeliveryDate: '2024-01-01', maxDeliveryDate: '2026-01-01', } ] }); session.completeShippingAddressChange({ updatedPaymentRequest: updatedPaymentRequest }); }); ``` ```javascript session.addEventListener("deliverymethodchanged", async (ev) => { const currentPaymentRequest = session.paymentRequest; const selectedDeliveryMethod = ev.deliveryMethod; let updatedRequestValues; if (selectedDeliveryMethod) { updatedRequestValues = {shippingLines: [{ label: selectedDeliveryMethod.label, amount: selectedDeliveryMethod.amount, code: selectedDeliveryMethod.code }], totalShippingPrice: { finalTotal: { amount: selectedDeliveryMethod.amount.amount, currencyCode: "USD", }, }, total: { amount: 20 + selectedDeliveryMethod.amount.amount, currencyCode: "USD" }} } else { updatedRequestValues= {total: { amount: 20, currencyCode: "USD" }} } // Update the payment request based on the delivery method change // and update the totals accordingly const updatedPaymentRequest = window.ShopPay.PaymentRequest.build({ ...currentPaymentRequest, ...updatedRequestValues, }); session.completeDeliveryMethodChange({ updatedPaymentRequest: updatedPaymentRequest }); }); ``` ```javascript session.addEventListener("pickuplocationchanged", async (ev) => { const currentPaymentRequest = session.paymentRequest; const pickupLocation = ev.pickupLocation; // Update the payment request based on the pickup location change // and update the totals accordingly const updatedPaymentRequest = window.ShopPay.PaymentRequest.build({ ...currentPaymentRequest, totalShippingPrice: { finalTotal: { amount: pickupLocation.amount.amount, currencyCode: "USD", }, }, total: { amount: 20 + pickupLocation.amount.amount, currencyCode: "USD" }, }); session.completePickupLocationChange({ updatedPaymentRequest: updatedPaymentRequest }); }); ``` ```javascript session.addEventListener("pickuplocationfilterchanged", async (ev) => { const currentPaymentRequest = session.paymentRequest; const buyerLocation = ev.buyerLocation; // Update the payment request based on the pickup location filter change // by filtering the available pickup locations based on a customer's location const updatedPaymentRequest = window.ShopPay.PaymentRequest.build({ ...currentPaymentRequest, pickupLocations: [{ label: "620 King Street West", code: "PICK_UP-KING-STREET-WEST", detail: "620 King Street West, Toronto, ON", amount: { amount: 10.00, currencyCode: "USD" }, readyExpectationLabel: "Ready in 1 hour", proximityLabel: "Less than 1 km away", }] }); session.completePickupLocationFilterChange({ updatedPaymentRequest: updatedPaymentRequest }); }); ``` ```javascript session.addEventListener("discountcodechanged", async (ev) => { const currentPaymentRequest = session.paymentRequest; const selectedDiscountCodes = ev.discountCodes; // Array of discount codes ["example-code-1"] // Update the payment request based on the discount code change // Let's assume the discount code is valid and the discount is 15% off const updatedPaymentRequest = window.ShopPay.PaymentRequest.build({ ...currentPaymentRequest, discountCodes: selectedDiscountCodes, lineItems: [ { label: "T-Shirt", finalItemPrice: { amount: 10.00, currencyCode: "USD" }, quantity: 2, sku: "t-shirt", requiresShipping: true, finalLinePrice: { amount: 20.00, currencyCode: "USD" }, } ], subtotal: { amount: 20.00, currencyCode: "USD" }, discounts: [ { label: "example-code-1", amount: { amount: 3.00, // Discounts must be passed to Shopify as a positive value currencyCode: "USD" } } ], totalTax: { amount: 1.06, currencyCode: "USD" }, total: { amount: 18.06, currencyCode: "USD" } }); session.completeDiscountCodeChange({ updatedPaymentRequest: updatedPaymentRequest }); }); ``` Confirm the payment once the user clicks the *Pay now* button in the Shop Pay popup. The server confirmation must invoke the `ShopPayPaymentRequestSessionSubmit` mutation to confirm that the payment is to be processed. Use [`ShopPayPaymentRequestSessionSubmit`](https://shopify.dev/docs/api/commerce-components/pay/graphql-pre-payment#example-shoppaypaymentrequestsessionsubmit) on your server to submit the session. ```javascript session.addEventListener("paymentconfirmationrequested", async (ev) => { // The customer's billing address contains relevant contact details such as email & phone number (if available) const billingAddress = ev.billingAddress; // Before submitting the payment request for processing, a final check should be done on your server // to make sure the payment request (total price, inventory available, etc.) is still valid. const response = fetch('/replace_with_your_endpoint', { method: 'POST', body: JSON.stringify({ token: session.token, payment_request: session.paymentRequest }), headers: { 'Content-Type': 'application/json', }, }).then(response => response.json()).then(data => { if (data.errors) { // Handle errors here. // For example: if an item is no longer in stock, you can send a new paymentRequest without that lineItem by building a new payment request and including it in completePaymentConfirmationRequest along with the errors. session.completePaymentConfirmationRequest({ errors: [ { "type": "generalError", "message": "Something went wrong. Please try again." } // Optionally build an updated paymentRequest and include it here ] }) } else { // confirm the payment request is processing session.completePaymentConfirmationRequest(); } }); }); ``` This event is dispatched when the payment is complete. Close the Shop Pay popup and redirect the user to the order confirmation page. ```javascript session.addEventListener("paymentcomplete", async (ev) => { console.log(ev.processingStatus.status); session.close(); // close the Shop Pay popup window.location.href = "/thank-you"; }); ``` This event is dispatched when a payment attempt fails. The event contains information about why the payment failed. ```javascript session.addEventListener("paymentattemptfailed", async (ev) => { const { reason, errorCode } = ev.error; console.log(`Payment attempt failed: ${reason}`); console.log(`Error code: ${errorCode}`); }); ``` This event is dispatched when the checkout window is closed. ```javascript session.addEventListener("windowclosed", async () => { // handle window closed event }); ``` **Warning:** Only call the corresponding complete call once for each event. ***