web 端对接 applepay

这方面国内的文档很少,也踩了不少的坑,今天有空记录一下

  1. 准备工作,商户配置,沙盒测试账号配置,需要在applepay 开发者 官网中配置,测试卡号也需要使用官方的沙盒 测试账号;
  2. 需要将官网的证书下载下来,一同放到服务器,证书有过期时间
  3. 前端需要在https 的环境下,才能进行调试,本地调试需要搭建环境,nginx 之类的进行转发

直接上前端代码

import PropTypes from 'prop-types';
import { useEffect, useState } from 'react';
import scriptLoader from 'react-async-script-loader';
import { useSelector } from 'react-redux';

// 这是后端提供的一个接口,获取applepay的token,需要拿到这个token 做鉴权;
import getApplePaySession from 'services/controller/checkout/getApplePaySession';

import { ButtonWrapper } from './ApplePayButton.style';

let applePayPayment = {};

function ApplyPayButton({
  isScriptLoaded,
  isScriptLoadSucceed,
  onSave,
  initiateResponse = {},
}) {
  const [showButton, setShowButton] = useState(false);
  const ckeckoutUpdate = {
    newTotal: {
      label: 'demo',
      amount:totalToPay,
    },
    newLineItems: [
      {
        label: 'Product Amount',
        amount:productAmount,
      },
      {
        label: 'Shipping Fee',
        amount: shippingFee,
      },
      {
        label: 'Tax',
        amount: totalTax,
      },
    ],
  };

  function onApplePayButtonClicked() {
    try {
      const request = {
        countryCode:  '',
        currencyCode: '',
        merchantCapabilities: [],
        supportedNetworks:  [],
        total: {
          label: label,
          type: 'final',
          amount: totalToPay,
        },
      };
      console.log('apple payment request', request);
      console.log('storeInfo', storeInfo);
      // Create ApplePaySession
      // eslint-disable-next-line no-undef
      const session = new ApplePaySession(3, request);

      session.onvalidatemerchant = async (event) => {
        console.log('event', event);
        // Call your own server to request a new merchant session.
        const hostName = window.location.host;
        const merchantSession = await getApplePaySession(
          applePayConfig,
          event.validationURL,
          hostName
        );
        console.log('merchantSession', merchantSession);
        session.completeMerchantValidation(merchantSession);
      };

      session.onpaymentmethodselected = (event) => {
        console.log('paymentChange', event);
        // Define ApplePayPaymentMethodUpdate based on the selected payment method.
        // No updates or errors are needed, pass an empty object.
        session.completePaymentMethodSelection(ckeckoutUpdate);
      };

      session.onshippingmethodselected = (event) => {
        console.log('shippingMethodChange', event);
        // Define ApplePayShippingMethodUpdate based on the selected shipping method.
        session.completeShippingMethodSelection(ckeckoutUpdate);
      };

      session.onshippingcontactselected = (event) => {
        console.log('onshippingcontactselected', event);
        // Define ApplePayShippingContactUpdate based on the selected shipping contact.
        session.completeShippingContactSelection(ckeckoutUpdate);
      };

      session.onpaymentauthorized = (event) => {
        const billingAddressInfo = {
          countryId:  'US',
          zipCode: '',
          city:  '',
          lastName:  '',
          state:  '',
          firstName:  '',
          addressLine2:  '',
          addressLine1: '',
        };

        console.log('Success1', event.payment);
        // eslint-disable-next-line no-undef
        console.log('SuccessStatus', ApplePaySession.STATUS_SUCCESS);
        const result = {
          // eslint-disable-next-line no-undef
          status: ApplePaySession.STATUS_SUCCESS,
        };
        applePayPayment = {
          applePayEncryptedPaymentBundle: {
            data: event.payment?.token.paymentData?.data,
            signature: event.payment?.token.paymentData.signature,
            header: event.payment?.token.paymentData.header,
            protocolVersion: event.payment?.token.paymentData.version,
          },
          digitalWalletType: 'ApplePay',
          digitalWalletLatitudeLongitude: '1,1',
          cardType:
            event.payment?.token?.paymentMethod?.network === 'AmEx'
              ? 'Amex'
              : event.payment?.token?.paymentMethod?.network,
          billingAddressInfo,
        };
        console.log('applyPayPayment', applePayPayment);
        onSave(applePayPayment);
        session.completePayment(result);
      };

      session.oncancel = (event) => {
        // Payment cancelled by WebKit
        console.log('cancel', event);
      };

      session.begin();
    } catch (e) {
      console.log('error', e);
    }
  }

  useEffect(() => {
    if (isScriptLoaded && isScriptLoadSucceed) {
      // eslint-disable-next-line no-undef
      if (window.ApplePaySession && ApplePaySession.canMakePayments) {
        setShowButton(true);
      }
      //   onApplePayButtonClicked();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isScriptLoadSucceed, isScriptLoaded]);

  return (
    <ButtonWrapper>
      {showButton && (
        <>
          <button
            onClick={onApplePayButtonClicked}
            type='button'
            label
            className='apple-pay-button'
            style={{ width: '293px', height: '50px' }}
          />
        </>
      )}
    </ButtonWrapper>
  );
}

ApplyPayButton.defaultProps = {
  isScriptLoaded: false,
  isScriptLoadSucceed: false,
};

ApplyPayButton.propTypes = {
  onSave: PropTypes.func.isRequired,
  isScriptLoaded: PropTypes.bool,
  isScriptLoadSucceed: PropTypes.bool,
  initiateResponse: PropTypes.func,
};

export default scriptLoader(['https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js'])(
  ApplyPayButton
);

代码基本都是官网的代码,这个比官网看的更直接一点;

  1. window.ApplePaySession && ApplePaySession.canMakePayments 判断浏览器是否支持
  2. getApplePaySession 这个是自己封装的方法,核心代码就是调用后端接口,取回数据,数据结构applepay 官网有,返回一样的结构体就行
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容