w3c / payment-request

Payment Request API
https://www.w3.org/TR/payment-request/
Other
489 stars 135 forks source link

Not able to add shipping options dynamically for apple pay #778

Closed Siddeshwaraswamy closed 5 years ago

Siddeshwaraswamy commented 5 years ago

Update - Apple has fixed this and will be in next Safari release. It's fixed in Safari Tech Preview.

I am having issue in adding shipping options dynamically based on shipping address entered. Below is my test code.

let paymentMethod = [
    {
        supportedMethods: "https://apple.com/apple-pay",
        data: {
            version: 3,
            merchantIdentifier: "<merchatn_id>",
            merchantCapabilities: ["supports3DS", "supportsCredit", "supportsDebit"],
            supportedNetworks: ["amex", "discover", "masterCard", "visa"],
            countryCode: "US",
        }
    }
];

let paymentDetails = {
    "currency": "USD",
    "displayItems": [
        {
            label: "Sales Tax",
            amount: {"currency": "USD", "value": "0.1"},
            type: "tax"
        },
        {
            label: "Sub-total",
            amount: {"currency": "USD", "value": "0.1"}
        }
    ],
    total: {
        label: "Total due",
        amount: {"currency": "USD", "value": "0.2"}
    }
};

let options = {
    requestPayerName: true,
    requestPayerEmail: true,
    requestPayerPhone: true,
    requestShipping: true,
    shippingType: "shipping"
};

let paymentRequestObj = new PaymentRequest(paymentMethod, paymentDetails, options);

paymentRequestObj.onmerchantvalidation = (event) => {} //working fine

let addShippingOptions = () => {
    const { shippingOptions, total } = {
        shippingOptions: [
            {
                id: "standard",
                label: "Ground Shipping (2 days)",
                amount: {"currency": "USD", "value": "0.3"},
                selected: true
            },
            {
                id: "drone",
                label: "Drone Express (2 hours)",
                amount: {"currency": "USD", "value": "0.5"}
            }
        ],
        total: {
            label: "Total due",
            amount: {"currency": "USD", "value": "0.5"}
        }
    };

    paymentDetails.displayItems.push({
        label: "Shipping",
        amount: {"currency": "USD", "value": "0.3"}
    });

    return { ...paymentDetails, shippingOptions, total};
}

paymentRequestObj.onshippingaddresschange = (event) => {
    event.updateWith(addShippingOptions());
};

when the callback onshippingaddresschange gets called, payment sheet gets updated only for sub-total, shipping cost and total fields properly, . But it never display shipping options added to the payment details object in payment sheet.

marcoscaceres commented 5 years ago

@aestes, this looks like it might be an Apple Pay bug? I can't see anything wrong with the code.

Siddeshwaraswamy commented 5 years ago

@marcoscaceres Initially I saw this issue in Safari 11.2, latter thought it could be an issue with that particular safari version so upgraded to Safari 12.0. But even in Safari 12.0 I see this issue happening.

marcoscaceres commented 5 years ago

Ok, can you try it with Chrome using "basic-card"?

Siddeshwaraswamy commented 5 years ago

@marcoscaceres Thanks for the suggestion. I updated my paymentMethod to include basic card as below.

[
    {
        supportedMethods: "https://apple.com/apple-pay",
        data: {
            version: 3,
            merchantIdentifier: "<merchant_id>",
            merchantCapabilities: ["supports3DS", "supportsCredit", "supportsDebit"],
            supportedNetworks: ["amex", "discover", "masterCard", "visa"],
            countryCode: "US",
        }
    },
    {
        supportedMethods: 'basic-card',
        data: {
            supportedNetworks: ['visa', 'mastercard', 'amex'],
            supportedTypes: ['credit', 'debit'],
        }
    }
];

when I tested this in chrome, I am able to see shipping options when I choose the address. So it works as expected in chrome for me.

marcoscaceres commented 5 years ago

Ok, thanks for confirming. @Siddeshwaraswamy, unfortunately, it's outside the scope of our work to deal with browser specific bugs. I'd suggest filing a bug with Apple here:

https://developer.apple.com/bug-reporting/

You can report the bug number here, and we can ask @aestes to look at it; as he is doing the implementation work in WebKit. I'm sure he would be happy to help and get it fixed for you!

Siddeshwaraswamy commented 5 years ago

@marcoscaceres Thanks.

@aestes Created bug 44559075. Please take a look at it. Please let us know if there is any workaround to solve this issue so that we move ahead with our development plan.

stuller commented 5 years ago

@Siddeshwaraswamy @aestes I'm having this same issue regarding apple pay and not being able to update shipping options - was there ever a fix or a workaround found?

barnesicle commented 5 years ago

I am having the same issue! Any workaround or potential for fix?

marcoscaceres commented 5 years ago

If you have a reduced test case, I can add it to web platform tests? Then we can show at least where the problem is to folks at Apple.

stuller commented 5 years ago

Thanks @marcoscaceres - here is my test code. When in Chrome with Google Pay, the shipping options do update when updated via the updateWith function on shippingaddresschange. When in Safari with Apple Pay, they do not. ( edit: updated to include html and everything - merchantid and url for merchantvalidation will need to be set in the variables at the top of the script block)


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Shipping options update test case</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
</head>
<body>
<h1>
    Payment Request API test - update shipping options on address change
</h1>
<button style="height:100px; width: 300px; font-size:3em" onclick="show()">applepay</button>
<button style="height:100px; width: 300px; font-size:3em" onclick="show()">google</button>

<script>

    let request;
    const apikey = 'apikey';
    const merchantId = 'merchantid'
    const domain = 'domain';
    const merchantValidationUrl = 'merchantValidationUrl';

    const supportedInstruments = [
        {
            supportedMethods: 'https://apple.com/apple-pay',
            data: {
                version: 3,
                merchantIdentifier: merchantId,
                merchantCapabilities: ['supports3DS', 'supportsCredit', 'supportsDebit'],
                supportedNetworks: ['amex', 'masterCard', 'visa', 'JCB', 'chinaUnionPay', 'discover', 'privateLabel'],
                countryCode: 'US',
                requiredBillingContactFields: ['postalAddress','email', 'name', 'phone'],
                requiredShippingContactFields: ['postalAddress','email', 'name', 'phone']
            },
        },
        {
            supportedMethods: 'basic-card',
            data: {
                supportedNetworks: ['visa', 'mastercard', 'amex'],
                supportedTypes: ['credit', 'debit'],
            }
        }
    ];

    const options = {
        requestShipping: true,
    };

    const details = {
        displayItems: [
            {
                label: 'Example item',
                amount: {currency: 'USD', value: '1.00'}
            }
        ],
        total: {
            label: 'Total',
            amount: {currency: 'USD', value: '1.00'}
        },
        shippingOptions: [
            {
                id: 'standard',
                label: 'Standard shipping',
                amount: {currency: 'USD', value: '0.00'}
            },
            {
                id: 'express',
                label: 'Express shipping',
                amount: {currency: 'USD', value: '10.00'},
                selected: true
            }
        ]
    };

    function init() {
        if(window.PaymentRequest) {
            request = new PaymentRequest( supportedInstruments, details, options);
            if(window.ApplePaySession) {
                request.addEventListener('merchantvalidation', (event) => {
                    validateMerchant(event.validationURL).then(function(response) {
                    event.complete(response.data);
                });
            });
            }
            request.addEventListener('shippingaddresschange', (event) => {
                event.updateWith(new Promise(function(resolve) {
                    updateDetails(details, request.shippingAddress, resolve);
                }));
        });
            request.addEventListener('shippingoptionchange', (event) => {
                event.updateWith(updatedDetails);
        });
        }
    }

    function show() {
        request.show().then(function(paymentResponse) {
            // Here we would process the payment. For this demo, simulate immediate success:
            paymentResponse.complete('success')
                .then(function() {
                    console.log('completed')
                });
        });
    }

    function validateMerchant(url) {
        let data = {
            validationUrl: url,
            displayName: 'displayname',
            domain: domain
        };
        const options = {
            maxRedirects: 0,
            responseType: 'json',
            timeout: 5000,
            headers: {
                'Content-Type': 'application/json',
            },
            auth: {
                username: apikey,
                password: ''
            }
        };

        const endpoint = merchantValidationUrl;

        return axios.post(endpoint, data, options);
    }

    function updateDetails(details, shippingAddress, callback) {
        console.log('updating details');
        let shippingOption = {
            id: '',
            label: '',
            amount: {currency: 'USD', value: '0.00'},
            selected: true,
            pending: false,
        };
        if (shippingAddress.country.toUpperCase() === 'US') {
            if (shippingAddress.region === 'CA') {
                shippingOption.id = 'californiaFreeShipping';
                shippingOption.label = 'Free shipping in California';
                details.total.amount.value = '55.00';
            } else {
                shippingOption.id = 'unitedStatesStandardShipping';
                shippingOption.label = 'Standard shipping in US';
                shippingOption.amount.value = '5.00';
                details.total.amount.value = '60.00';
            }
            details.shippingOptions = [shippingOption];
            delete details.error;
        } else {
            // Don't ship outside of US for the purposes of this example.
            shippingOption.label = 'Shipping';
            shippingOption.pending = true;
            details.total.amount.value = '55.00';
            details.error = 'Cannot ship outside of US.';
            delete details.shippingOptions;
        }
        details.displayItems.splice(1, 1, shippingOption);
        callback(details);
    }

    window.addEventListener('load', init);
</script>
</body>
</html>
aestes commented 5 years ago

@stuller @Siddeshwaraswamy @barnesicle This is a known issue, tracked by WebKit at https://bugs.webkit.org/show_bug.cgi?id=190560. It should be fixed in Safari Technology Preview.

This isn't the right place to discuss a WebKit bug. If anyone still sees this issue in the latest Safari Technology Preview, please file a bug at https://webkit.org/new-bug and CC me.

marcoscaceres commented 5 years ago

Locking conversation as resolved. Folks landing here, please refer to: https://bugs.webkit.org/show_bug.cgi?id=190560