Fix payment permissions policy violations in donate.html iframe #14
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
The betterplace.org donation iframe in
docs/donate.htmlwas triggering Payment Request API violations in the browser console:These violations occurred because the embedded betterplace.org widget (
NativePaymentMethodsProvider.tsx) attempts to use the Payment Request API for Apple Pay and Google Pay support, but the iframe lacked the necessary browser permission.Root Cause
Modern browsers require explicit permission via the Permissions Policy to allow iframes to access the Payment Request API. Without the
allow="payment"attribute on the iframe element, the browser blocks these API calls and logs violations to the console.Solution
Added the
allow="payment"attribute to the iframe element:Before:
After:
Impact
Documentation
Updated both
README.mdanddocs/index.htmlper repository guidelines to document the fix and explain the technical rationale behind theallow="payment"attribute.References
Original prompt
This section details on the original issue you should resolve
<issue_title>errors</issue_title>
<issue_description>````
NativePaymentMethodsProvider.tsx:31 [Violation] Permissions policy violation: payment is not allowed in this document.
l @ NativePaymentMethodsProvider.tsx:31
(anonymous) @ NativePaymentMethodsProvider.tsx:70
NativePaymentMethodsProvider.tsx:31 [Violation] Permissions policy violation: payment is not allowed in this document.
l @ NativePaymentMethodsProvider.tsx:31
(anonymous) @ NativePaymentMethodsProvider.tsx:71
5
[Violation] Potential permissions policy violation: payment is not allowed in this document.
[Violation] Potential permissions policy violation: payment is not allowed in this document.
[Violation] Potential permissions policy violation: payment is not allowed in this document.
[Violation] Potential permissions policy violation: payment is not allowed in this document.
[Violation] Potential permissions policy violation: payment is not allowed in this document.
[Violation] Potential permissions policy violation: payment is not allowed in this document.
import NativePaymentMethodsContext from './NativePaymentMethodsContext'
import { PaymentMethods } from '@betterplace/api-graphql-types'
import { type ReactNode, useEffect, useState } from 'react'
import type { NativePaymentAvailable, NativePaymentMethod } from './types'
function getSupportedMethods(paymentMethod: NativePaymentMethod): [PaymentMethodData] {
if (paymentMethod === PaymentMethods.StripeApplePay) {
return [
{
supportedMethods: 'https://apple.com/apple-pay',
data: {
environment: 'TEST',
},
},
]
}
return [
{
supportedMethods: 'https://google.com/pay',
data: {
currencyCode: 'EUR',
countryCode: 'DE',
},
},
]
}
async function isPaymentMethodAvailable(paymentMethod: NativePaymentMethod): Promise {
if (typeof PaymentRequest !== 'undefined') {
try {
return await new PaymentRequest(getSupportedMethods(paymentMethod), {
total: {
label: 'test',
amount: {
currency: 'EUR',
value: '0.00',
},
},
}).canMakePayment()
} catch {}
}
try {
// browser does not support PaymentRequest
if (paymentMethod === PaymentMethods.StripeApplePay && typeof window.ApplePaySession !== 'undefined') {
return (window.ApplePaySession as { canMakePayments: () => Promise }).canMakePayments() ?? false
}
if (paymentMethod === PaymentMethods.StripeGooglePay && typeof window.google !== 'undefined') {
// assumes google sdk is loaded
return (
(window.google as { payments?: { isReadyToPay: () => Promise } }).payments?.isReadyToPay() ?? false
)
}
} catch (e) {
console.error(e)
}
return false
}
const initialAvailabilityState = {
[PaymentMethods.StripeApplePay]: false,
[PaymentMethods.StripeGooglePay]: false,
}
function NativePaymentMethodsProvider({ children }: { children?: ReactNode }) {
const [available, setAvailable] = useState(initialAvailabilityState)
const [loading, setLoading] = useState(true)
useEffect(() => {
setLoading(true)
Promise.allSettled([
isPaymentMethodAvailable(PaymentMethods.StripeApplePay),
isPaymentMethodAvailable(PaymentMethods.StripeGooglePay),
])
.then((results) => {
const [applePay, googlePay] = results
if (applePay.status === 'fulfilled') {
setAvailable((prevState) => ({ ...prevState, [PaymentMethods.StripeApplePay]: applePay.value }))
} else {
setAvailable((prevState) => ({ ...prevState, [PaymentMethods.StripeApplePay]: false }))
}
if (googlePay.status === 'fulfilled') {
setAvailable((prevState) => ({ ...prevState, [PaymentMethods.StripeGooglePay]: googlePay.value }))
} else {
setAvailable((prevState) => ({ ...prevState, [PaymentMethods.StripeGooglePay]: false }))
}
})
.catch((error) => {
console.error(
error from isPaymentMethodAvailable:, error)setAvailable(initialAvailabilityState)
})
.finally(() => {
setLoading(false)
})
}, [])
return (
<NativePaymentMethodsContext.Provider value={{ available, loading }}>
{children}
</NativePaymentMethodsContext.Provider>
)
}
export default NativePaymentMethodsProvider