I have troubles sometimes (with slow internet connections) when the customers clicks on my Buy Now button, i got the error "Must crerate a new ApplePaySession from a user gesture hanlder..." I removed some logics to shipping to nt make this too long, but essentially my code is:
export default function ApplePayButton({setLoadingApple}) {
// Effects
React.useEffect(() => {
// listen click
const button = document.querySelector('apple-pay-button')
if(button){
button.addEventListener('click', startApplePaySession)
}
return () => {
button.removeEventListener('click', startApplePaySession)
}
}, [])
// Methods
const selectStep = step => {
// Check if step is available
if (state.steps.allowed >= step) {
dispatch({
type: 'UPDATE_STEPS',
data: {
...state.steps,
current: step
}
})
}
}
const startApplePaySession = () => {
const apple = window.buckedup.payment_extensions.find(extension => extension.code === 'apple_pay')
if (window.ApplePaySession && apple) {
setLoadingApple(true)
let clientToken
if (window.braintree_config) {
const {
token
} = window.braintree_config
clientToken = token
}
window.braintree.client.create({
authorization: clientToken
}, (clientErr, clientInstance) => {
if (clientErr) {
setLoadingApple(false)
console.error('Error creating client:', clientErr)
return
}
window.braintree.applePay.create({
client: clientInstance
}, (applePayErr, applePayInstance) => {
if (applePayErr) {
setLoadingApple(false)
console.error('Error creating applePayInstance:', applePayErr)
return
}
const amount = state.totals.amount_due.amount
const appleSession = window.ApplePaySession
// Build request object
const request = applePayInstance.createPaymentRequest({
'countryCode': 'US',
'currencyCode': 'USD',
'merchantCapabilities': [
'supports3DS',
'supportsDebit',
'supportsCredit'
],
'shippingMethods': [],
'shippingType': 'shipping',
'supportedNetworks': [
'visa',
'masterCard',
'amex',
'discover'
],
'requiredBillingContactFields': [
'postalAddress',
'name'
],
'requiredShippingContactFields': [
'postalAddress',
'name',
'phone',
'email'
],
'total': {
'label': 'Bucked Up',
'amount': amount,
'type': 'final'
}
})
// Define ApplePayPaymentRequest
const session = new appleSession(3, request)
// Validate merchant
session.onvalidatemerchant = async event => {
// Do request
const response = await getApplePay()
applePayInstance.performValidation({
validationURL: event.validationURL,
displayName: 'Bucked Up'
}, (err, merchantSession) => {
if (err) {
setLoadingApple(false)
return
}
session.completeMerchantValidation(response.data)
setLoadingApple(false)
})
}
// Validate payment method
session.onpaymentmethodselected = async event => {
// Update totals
const totals = await postApplePayment()
const update = {
'newTotal': {
'label': 'Bucked Up',
'amount': totals.data.amount_owed
}
}
session.completePaymentMethodSelection(update)
}
// Request to track shipping user data
session.onshippingcontactselected = async event => {
// Extract user available
const dataShipping = event.shippingContact
let body
// Do first request to checkout endpoint
const responseData = await shippingService(body)
if (responseData.success) {
// Get Shipping methods
const body = {
combined_shipments: true
}
const responseMethods = await getMethods(body)
if (responseMethods.success) {
const methods = responseMethods.data.shipments
// Do request to update totals
const responseTotals = await postApplePayment()
const update = {
'newTotal': {
'label': 'Bucked Up',
'amount': responseTotals.data.amount_owed
},
'newShippingMethods': mappedMethods
}
session.completeShippingContactSelection(update)
}
}
}
// Authorize purchase
session.onpaymentauthorized = async (event) => {
applePayInstance.tokenize({
token: event.payment.token
}, async (tokenizeErr, payload) => {
if (tokenizeErr) {
console.error('Error tokenizing Apple Pay:', tokenizeErr)
session.completePayment(appleSession.STATUS_FAILURE)
return
}
const nonce = payload.nonce
const billing = event.payment.billingContact
const shipping = event.payment.shippingContact
const body = {
shipping,
billing
}
const response = await patchApplePayment(body)
if (response.success) {
// Process payment
const bodyBraintree = {
apple_pay: true,
payment_method_nonce: nonce
}
const responseBraintree = await postBraintree(bodyBraintree)
if (responseBraintree.success) {
// Define ApplePayPaymentAuthorizationResult
const result = {
'status': appleSession.STATUS_SUCCESS
}
session.completePayment(result)
location.href = `${API_URL}checkout/thank-you`
}
}
})
}
session.oncancel = (event) => {
console.log(event, 'session cancel')
setLoadingApple(false)
selectStep(1)
}
session.begin()
}
)
})
}
}
return (
<apple-pay-button buttonstyle='black' locale='en' type='plane' />
)
}