# Encrypted Card ## 介绍 Encrypted Card 用于收集并加密卡号、过期时间和 CVV 信息。如果您使用自定义支付表单并希望符合 PCI 标准,建议采用 Encrypted Card。 该方式基于支付 API,接入前需完成商户身份校验,请确认以下条件: 1. 已注册商户并获取有效**客户端密钥**(Client key)。 2. 能够自行调用我们的支付 API。 ## 开始前准备 可以安装 PayKKa Checkout UI npm 包,或者通过 CDN 的方式嵌入到 HTML 中: npm(推荐) 安装 `@paykka/card-checkout-ui`: npm ```bash npm i @paykka/card-checkout-ui ``` pnpm ```bash pnpm i @paykka/card-checkout-ui ``` yarn ```bash yarn add @paykka/card-checkout-ui ``` 为了正常的展示组件,需要全局引入样式: ```typescript import '@paykka/card-checkout-ui/style.css' ``` CDN 引入 通过 CDN 链接在 HTML 中引入 Encrypted Card 样式和脚本,推荐使用对应区域的 CDN 链接: 欧洲 ```html ``` 香港 ```html ``` ## 创建 DOM 在您的收银台页面创建 DOM 元素,该元素用于呈现 Encrypted Card。 Encrypted Card 支持将卡号、过期时间和 CVV **合并**或**分开**展示,您需要在 HTML 中预先定义好。 无论您选择如何展示,都需要使用 `id` 为 `encryptedCardWrapper` 的祖先元素将放置子表单域的元素包裹,`data-eci` 则表示对应表单域名称。 下面是两种展示方式的 DOM 定义示例: 分开展示 ```html
``` 合并展示 ```html
``` Encrypted Card 会根据元素的 `id` 和 `data-eci` 属性将表单域插入到指定位置,请务必对元素设置正确的属性名和值。 ## 构建 Encrypted Card ### 构建参数 创建 Encrypted Card 实例时可传递参数及事件如下: 必填参数用 * 标识 | **名称** | **说明** | **类型** | | --- | --- | --- | | *merchantId | 商户 ID。 | `string` | | *clientKey | 客户端密钥。 | `string` | | locale | 表单项所展示的语言。- 不传则获取浏览器语言,若浏览器语言不在支持范围内,则默认使用 `en-GB`。 - 目前支持如下 11 种国际化语言,按照 IETF BCP 47 格式展示: - `de-DE`:德语(德国) - `en-GB`:英语(英国) - `es-ES`:西班牙语(西班牙) - `fr-FR`:法语(法国) - `ja-JP`:日语(日本) - `ko-KR`:韩语(韩国) - `pt-PT`:葡萄牙语(葡萄牙) - `ru-RU`:俄语(俄罗斯) - `zh-CN`:中文(中国) - `zh-HK`:中文(香港) - `zh-TW`:中文(台湾) | `string` | | brands | 有效的[卡品牌](#%E5%8D%A1%E5%93%81%E7%89%8C)数组。- 身份验证完成后会获取商户可用的卡品牌,然后两者取交集。 - 如果不传,则直接取商户可用卡品牌。 | `string` | | styles | 输入框、label 和错误提示文字的[样式](#%E8%AE%BE%E7%BD%AE-encrypted-card-%E6%A0%B7%E5%BC%8F)。 | `ElementStylesConfig` | | showLabel | 是否展示表单项 label,默认为 `true`。 | `boolean` | | sandbox | 是否使用 Sandbox 环境。 | `boolean` | | onReady | 身份校验完成后调用,并返回校验结果,若校验成功则返回可用[卡品牌](#%E5%8D%A1%E5%93%81%E7%89%8C)。 | `(status: boolean, res?: { brands: string[] }) => void` | | onActivated | 表单域渲染完成后触发,并返回对应[表单域类型](#%E8%A1%A8%E5%8D%95%E5%9F%9F%E7%B1%BB%E5%9E%8B)。 | `(fieldType: FieldType) => void` | | onFocus | 表单域获得焦点时触发,并返回对应[表单域类型](#%E8%A1%A8%E5%8D%95%E5%9F%9F%E7%B1%BB%E5%9E%8B)。 | `(fieldType: FieldType) => void` | | onBlur | 表单域失去焦点时触发,并返回对应[表单域类型](#%E8%A1%A8%E5%8D%95%E5%9F%9F%E7%B1%BB%E5%9E%8B)。 | `(fieldType: FieldType) => void` | | onBinChanged | 卡 bin 信息改变时触发。- 返回包含卡号前 1-6 位的值和[卡品牌](#%E5%8D%A1%E5%93%81%E7%89%8C)。 - 如果用户删除卡号,或卡号不足 6 位,则 `binValue` 为空。 | `(binInfo: { binValue: string; brand?: CardBrand }) => void` | | onBrand | 检测到卡品牌时调用,返回对应[卡品牌](#%E5%8D%A1%E5%93%81%E7%89%8C)。 | `(brand?: CardBrand) => void` | | onCardEncrypted | 卡信息加密成功后触发,返回[加密结果](#%E5%8A%A0%E5%AF%86%E7%BB%93%E6%9E%9C)。 | `(encryptedInfo: EncryptCardRes) => void` | | onCardEncryptionFailed | 卡信息加密失败后触发。 | `() => void` | | onValidationChanged | 通知表单项验证信息,返回对应[表单域类型](#%E8%A1%A8%E5%8D%95%E5%9F%9F%E7%B1%BB%E5%9E%8B)和验证状态。 | `(validationInfo: { fieldType: FieldType; status: FormValidationStatus) }) => void` | ### 初始化 调用 `init` 方法,携带 Encrypted Card 构建参数进行初始化: npm ```javascript import { PayKKaEncryptedCard } from '@paykka/card-checkout-ui' const EncryptedCard = PayKKaEncryptedCard.init({ // [!code highlight] merchantId: 'xxx', clientKey: 'xxx', brands: ['VISA', 'MASTER_CARD'], showLabel: true, styles: { input: { base: { fontSize: '16px' }, focus: { color: 'blue' }, valid: { border: '1px solid yellowgreen', color: 'yellowgreen' }, invalid: { border: '1px solid red', color: 'red' } } } }) ``` CDN ```javascript const PayKKaEncryptedCard = window.PayKKaCardCheckoutEncryptedCard const EncryptedCard = PayKKaEncryptedCard.init({ // [!code highlight] merchantId: 'xxx', clientKey: 'xxx', brands: ['VISA', 'MASTER_CARD'], showLabel: true, styles: { input: { base: { fontSize: '16px' }, focus: { color: 'blue' }, valid: { border: '1px solid yellowgreen', color: 'yellowgreen' }, invalid: { border: '1px solid red', color: 'red' } } } }) ``` ## 设置 Encrypted Card 样式 Encrypted Card 自带默认样式,您可以自定义输入框、label 和错误提示文案下对应状态的样式,初始化时传递 `styles` 即可。 `styles` 对象类型如下,`CSSStyleDeclaration` 表示我们支持绝大部分符合标准的样式: ```typescript interface ElementStylesConfig { /** input 元素的样式 */ input?: InputStylesConfig /** label 文案的样式 */ label?: StylesConfig /** 错误提示文案的样式 */ errorMessage?: Partial } interface StylesConfig { /** 默认样式 */ base?: Partial /** 校验通过时样式 */ valid?: Partial /** 校验不通过时样式 */ invalid?: Partial /** 聚焦元素时样式 */ focus?: Partial } interface InputStylesConfig extends StylesConfig { /** placeholder样式 */ placeholder?: { /** 默认样式 */ base?: Partial /** 聚焦元素时样式 */ focus?: Partial } /** 将鼠标悬停在元素上的样式 */ hover?: Partial } ``` 下面是一个示例: ```javascript const EncryptedCard = PayKKaEncryptedCard.init({ styles: { input: { base: { fontSize: '20px', fontWeight: 'bold', padding: '0 0 0 12px', borderRadius: '0px', boxShadow: 'none' }, focus: { color: 'blue', caretColor: 'blue', border: '1px solid blue' }, valid: { border: '1px solid yellowgreen', color: 'yellowgreen' }, invalid: { border: '1px solid red', color: 'red' } }, label: { base: { fontSize: '20px', fontWeight: 'bold', margin: '0', color: 'purple' }, valid: { color: 'yellowgreen' }, invalid: { color: 'red' }, focus: { color: 'blue' } }, errorMessage: { fontSize: '20px', fontWeight: 'bold', margin: '0', color: 'yellow' } } }) ``` 除输入框、label 和错误提示文案外,如果您想更改其他样式,可以直接编写 CSS 代码覆盖我们的默认样式。 ## 加密结果 提交表单的时候,您需要调用 `encrypt` 方法,如果表单验证通过,我们会对表单信息进行加密: ```html ``` ```javascript const handleClick = () => { // 加密 EncryptedCard.encrypt() // [!code highlight] } ``` 由于加密过程是异步的,您需要通过注册 `onCardEncrypted` 事件回调获取加密结果: ```javascript let encryptedRes = null const EncryptedCard = PayKKaEncryptedCard.init({ // 此处省略其他属性 onCardEncrypted: res => { // [!code highlight] console.log('加密成功', res) encryptedRes = res processPay() } }) const processPay = () => { // 调用支付 API 并传递加密信息 encryptedRes } ``` 加密结果类型定义如下: ```typescript interface EncryptCardRes { /** 加密后的卡号 */ encryptedCardNumber: string /** 加密后的 CVV */ encryptedCVV: string /** 加密后的有效期年份 */ encryptedExpireYear: string /** 加密后的有效期月份 */ encryptedExpireMonth: string /** 卡信息 */ cardInfo: { /** 卡 bin */ bin: string /** 卡品牌 */ brand: CardBrand /** 卡号后 4 位 */ last4: string } } ``` ## 部分属性说明 ### 卡品牌 **`CardBrand`** 目前我们支持的卡品牌枚举和对应卡品牌如下: ```typescript type CardBrand = keyof typeof CardBrandCode enum CardBrandCode { /** Visa */ VISA = 'VISA', /** Mater Card */ MASTER_CARD = 'MASTER_CARD', /** JCB */ JCB = 'JCB', /** American Express */ AMEX = 'AMEX', /** Discover */ DISCOVER = 'DISCOVER', /** Diners Club */ DINERS_CLUB = 'DINERS_CLUB' } ``` ### 表单域类型 **`FieldType`** 表单类型枚举及对应描述如下: ```typescript type FieldType = keyof typeof EFieldType enum EFieldType { /** 卡号 */ CARD_NUMBER = 'CARD_NUMBER', /** cvv */ CVV = 'CVV', /** 过期时间 */ EXPIRE_DATE = 'EXPIRE_DATE' } ``` ### 验证状态 **`FormValidationStatus`** 表单验证状态分为三种,状态值和对应描述如下: - `unValidate`: 内容为空。 - `success`: 校验成功。 - `error`: 校验失败。 ```typescript type FormValidationStatus = 'unValidate' | 'success' | 'error' ``` ## 示例 下面是从初始化表单域到完成加密的代码示例: ```html
``` ```javascript let encryptedRes = null const EncryptedCard = PayKKaEncryptedCard.init({ merchantId: 'xxx', clientKey: 'xxx', brands: ['VISA', 'MASTER_CARD'], showLabel: true, styles: { input: { base: { fontSize: '16px' }, focus: { color: 'blue' }, valid: { border: '1px solid yellowgreen', color: 'yellowgreen' }, invalid: { border: '1px solid red', color: 'red' } } }, onCardEncrypted: res => { console.log('加密成功', res) encryptedRes = res processPay() } }) const handleClick = () => { // 加密 EncryptedCard.encrypt() } const processPay = () => { // 调用支付 API 并将加密信息传入 } ``` ## Sandbox 环境接入 Sandbox 环境下接入,需要引入 Sandbox 环境的 CDN,并在初始化时设置 `sandbox` 为 `true`。 ```html ``` ```javascript const EncryptedCard = PayKKaEncryptedCard.init({ // 此处省略其他属性 sandbox: true // [!code highlight] }) ```