# waft-payment

> Payment components for Waft App.

Latest version **0.1.3** (published 2022-05-31) · MIT license · 0 weekly downloads

## Install

```sh
npm install waft-payment
pnpm add waft-payment
yarn add waft-payment
bun add waft-payment
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.1.3 |
| Published | 2022-05-31 |
| First published | 2022-02-09 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 3 |
| Unpacked size | 44.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | aligenie-skill |
| Maintainers | aligenie-skill |

## Links

- npm: https://www.npmjs.com/package/waft-payment
- npm.io page: https://npm.io/package/waft-payment

## Dependencies (3)

- [waft](https://npm.io/package/waft.md) ^0.7.x
- [waft-ui](https://npm.io/package/waft-ui.md) ^0.2.x
- [waft-json](https://npm.io/package/waft-json.md) ^1.1.10

## Recent versions

- 0.1.3 (latest) — 2022-05-31
- 0.1.2 — 2022-02-16
- 0.1.1 — 2022-02-16
- 0.1.0 — 2022-02-09

## README

# Waft 收银台

为 [Waft](https://www.yuque.com/waft/docs) 应用提供收银台能力。

## 功能特性
- 支持 **支付宝** 扫码支付
- 支持两种展示形式
  - 内嵌：极简的二维码样式
  - 浮窗：内置统一的浮窗样式，除了二维码，同时展示更多的订单信息（订单金额、商户名称等）

## 接入说明

- 接入收银台之前，需完成[天猫精灵应用商业化入驻流程](https://aligenie.com/doc/20255408/dqh51i)。
- Waft 容器版本要求：>= 1.3。[如何判断当前容器版本？](https://www.yuque.com/waft/docs/ttm1gn)

## 安装
`npm install --save waft-payment`

## API

### 参数说明

| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| sellerId | i64 | 是 | 商户淘宝 ID |
| content | string | 是 | 支付内容（内容的字段说明见下方），以 **JSON 字符串**表示 |
| sign | string | 是 | 根据商户 RSA 私钥对 `content` 的签名，平台会根据商户提交的公钥进行验签（加签、验签方式见下方） |
| size | i64 | 否 | （仅内嵌形式下可用）二维码的宽高，默认为 200 |
| onStatus | Function | 否 | 订单支付状态回调（状态枚举值见下方） |
| requestNewOrder | Function | 是 | （仅浮窗形式下可用）请求订单刷新时，该回调会执行 |

### `content` 支付内容字段说明：

| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| sellerId | i64 | 是 | 商户淘宝 ID |
| amount | i64 | 是 | 订单金额 **（单位：分）** |
| outItemId | string | 是 | 商户的商品 ID |
| outItemName | string | 是 | 商户售卖的商品名称 |
| outOrderId | string | 是 | 商户订单号 |
| payExpireTime | string | 是 | 订单的超时时间（如：2049-12-30 23:59:59） |

### 订单状态（`status`）说明：

- 等待未超时：

| 状态值 | 说明 |
|---|---|
| 100 | 天猫精灵侧订单创建成功 |
| 200 | 交易创单成功 |
| 250 | 交易创单失败 |
| 300 | 支付创单成功 |
| 350 | 支付创单失败 |
| 400 | 用户支付成功 |
| 450 | 用户支付失败 |
| 470 | 用户待支付 |


``` typescript
this.addEventListener('onStatus', (e: Event) => {
  const current = (e as MessageEvent).data
  // { "status": 300 }
  // { "status": 350, "failMsg": "订单创建失败的原因" }
  console.log(current)
})
```

- 等待超时：

> **超时**指的是订单创建成功后，长时间处于**未支付**状态。目前的等待时长约 3-4 分钟，超过这个时间，需创建新的订单。

``` typescript
this.addEventListener('onStatus', (e: Event) => {
  const current = (e as MessageEvent).data
  // { "isTimeout": true }
  console.log(current)
})
```

## 如何使用

在需要使用支付组件的页面（比如 `pages/index.json`）中，声明以下组件：
- `x-payment-qrcode` 以内嵌形式展示支付二维码
- `x-payment-drawer` 以浮窗形式展示支付二维码

``` json
{
  "usingComponents": {
    "x-payment-qrcode": "waft-payment/assembly/qrcode/qrcode",
    "x-payment-drawer": "waft-payment/assembly/drawer/drawer"
  }
}
```

在 `pages/index.json` 中添加相关业务逻辑处理：

``` typescript
import { Page, Props, Event, MessageEvent, console } from 'waft'
import { JSONObject } from 'waft-json'
import { PaymentDrawer } from 'waft-payment'

export class Demo extends Page {
  constructor(props: Props) {
    super(props)

    const state = new JSONObject()
    state.set('sellerId', 0)
    state.set('content', '')
    state.set('sign', '')

    this.setState(state)

    // 创建订单，并渲染内嵌支付二维码
    this.addEventListener('createEmbeddedPayment', (e: Event, target) => {
      const _this = target as Demo
      _this.createEmbeddedQrCode()
    })

    // 创建订单，并打开浮窗展示支付二维码
    this.addEventListener('createPopupPayment', (e: Event, target) => {
      const _this = target as Demo
      _this.createPopupQrCode()
    })

    // （仅在浮窗形式下用到）点击刷新当前订单
    this.addEventListener('requestNewOrder', (e: Event, target) => {
      const _this = target as Demo
      _this.refreshPopupQrCode()
    })

    // 这里可获取当前订单的状态
    this.addEventListener('onStatus', (e: Event) => {
      const current = (e as MessageEvent).data
      // { "status": 300 }
      // { "status": 350, "failMsg": "订单创建失败的原因" }
      // { "isTimeout": true } 等待超时，订单仍未支付
      console.log(current)
    })
  }

  createEmbeddedQrCode(): void {
    /**
     * 注意：内嵌二维码 `didMount` 后不会响应后续 `props` 的更新
     * 如果你想更新订单，需先将组件卸载（比如，本例子中可将订单参数重置为空）
     * 然后根据新的参数 (content / sign) 再渲染新的组件
     */
    if (this.state.getString(sign) != '') {
      const nextState = new JSONObject()
      nextState.set('sellerId', 0)
      nextState.set('content', '')
      nextState.set('sign', '')
      this.setState(nextState)
    }

    setTimeout((_, target) => {
      const _this = target as Demo
      const orderParams = _this.generateOrderParams()
      _this.setState(orderParams)
    }, 0, this)
  }

  createPopupQrCode(): void {
    const orderParams = this.generateOrderParams()
    const sellerId = orderParams.getInteger('sellerId')
    const content = orderParams.getString('content')
    const sign = orderParams.getString('sign')

    PaymentDrawer.open(sellerId, content, sign)
  }

  refreshPopupQrCode(): void {
    const orderParams = this.generateOrderParams()
    const sellerId = orderParams.getInteger('sellerId')
    const content = orderParams.getString('content')
    const sign = orderParams.getString('sign')

    PaymentDrawer.refresh(sellerId, content, sign)
  }

  generateOrderParams(): JSONObject {
    const sellerId = 123 // 商户淘宝 ID
    
    const contentParams = new JSONObject()
  	contentParams.set('sellerId', sellerId) // 商户淘宝ID
    contentParams.set('amount', 1) // 订单金额：1 分钱
  	contentParams.set('outItemId', '123') // 商品 ID
  	contentParams.set('outItemName', '商品名称') // 商品名称
  	contentParams.set('outOrderId', 'abc') // 订单ID
    /**
     * 订单的过期时间：超过这个时间，该笔订单将不能被支付
     * 注意：每个商户的 “待支付订单” 有数量限制
     * 建议不要将过期时间设置得太久，以避免占用限额，导致无法创建新的订单
     */
  	contentParams.set('payExpireTime', '2049-12-30 23:59:59')

  	const content = contentParams.toString() // 该字段为 JSON 字符串形式
    
    // 根据 `content` 生成签名
  	const sign = 'xxx'

    const orderParams = new JSONObject()
    orderParams.set('sellerId', sellerId)
    orderParams.set('content', content)
    orderParams.set('sign', sign)

    return orderParams
  }
}
```

在 `pages/index.axml` 中使用组件：

``` xml

<!-- 渲染内嵌形式的支付二维码 -->
<view>
  <view onTap="createEmbeddedPayment">创建订单（内嵌二维码）</view>
  <x-payment-qrcode 
    a:if="{{ sellerId && content && sign }}" 
    sellerId="{{ sellerId }}" 
    content="{{ content }}" 
    sign="{{ sign }}"
    onStatus="onPaySuccess" 
  ></x-payment-qrcode>
</view>

<!-- 以浮窗形式打开支付二维码 -->
<view>
  <view onTap="createPopupPayment">创建订单（浮窗）</view>
  <x-payment-drawer 
    requestNewOrder="requestNewOrder" 
    onStatus="onPaySuccess"
  ></x-payment-drawer>
</view>
```

## 加签验签说明

``` java
// 通过支付宝 SDK 提供的工具类进行加签验签操作
// 可以在 maven 中央仓库下载到，版本选择 3.7.80.ALL 以上即可
// https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java

<dependency>
  <groupId>alipay-sdk-java</groupId>
  <artifactId>alipay-sdk-java</artifactId>
  <version>3.7.80.ALL</version>
</dependency>

// content：需要加签的业务参数，以 JSON 字符串表示

// 加签
String sign = AlipaySignature.rsaSign(content, sysPriKey, "UTF-8", "RSA");

// 验签
boolean signCheck = AlipaySignature.rsaCheck(content, sign2, sysPubKey, "UTF-8", "RSA");
```

## 支付状态 HTTP 回调

点击查看[参考文档](https://aligenie.com/doc/20255408/dqh51i)。

---
_Source: https://npm.io/package/waft-payment · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
