---
title: Sessions and events
description: >-
  Learn how to create payment request sessions and handle events in Shop Pay
  Wallet API.
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

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 `shopId` and `clientId` from your onboarding email to configure the API. For the full list of options, see [`configure` parameters](https://shopify.dev/docs/api/commerce-components/pay/javascript-sdk#configure-parameters).

```javascript
window.ShopPay.PaymentRequest.configure({
  shopId: 1,
  clientId: "[REPLACE-ME]",
});
```

***

## 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}`);
});
```

**Caution:**

Buyers have the option to select a different payment method to retry their checkout after a failure. Don't call `session.close()` so that users can retry with a new payment method without disrupting their session.

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.

***
