# 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]
})
```