npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@posx/yeahpay

v1.1.1

Published

YeahPay payment integration library

Readme

@posx/yeahpay

YeahPay(云 MISPOS)支付协议封装。库只抽象协议,轮询、重试、UI 全部留给业务。

✨ 特性

  • 🔐 报文加解密与签名(RSA-2048 + AES-128-CBC + SHA-256)
  • 🏦 银行卡、二维码主扫、二维码被扫
  • 🧭 支付状态判读:把协议原文归一成 pending / paid / unpaid
  • 💰 退款、当日撤销、取消订单、结算、重打印
  • 🧪 内置模拟,便于开发
  • 📱 基于 Capacitor HTTP,无 CORS 限制
  • 📝 TypeScript 类型完整

📦 安装

npm install @posx/yeahpay

需要 Capacitor 环境(v5/v6/v7)。若项目未安装:npm install @capacitor/core

🚀 快速开始

import { YeahPayClient, IYeahPayApiConfig } from '@posx/yeahpay'

const config: IYeahPayApiConfig = {
  asyncUrl: 'https://your-yeahpay-server.com/async',
  syncUrl: 'https://your-yeahpay-server.com/sync',   // 同步端点
  appId: 'YOUR_APP_ID',
  deviceSn: 'YOUR_DEVICE_SERIAL',
  serverPublicKey: 'YOUR_SERVER_RSA_PUBLIC_KEY',     // PKCS8 PEM
  clientPrivateKey: 'YOUR_CLIENT_RSA_PRIVATE_KEY',   // PKCS8 PEM
  signatureKey: 'YOUR_SIGNATURE_KEY'
}

const client = new YeahPayClient(config)

syncUrl 是必填项。走同步端点的有三个方法:createSyncCardChargequeryOrderSynccancelOrder。 缺了它,这三个方法会把请求打到字面量 undefined 拼成的地址上(库当前不校验,1.0.x 起即如此)。


🧭 支付状态判读(v1.1.0 新增)

这是本版本的核心。以前业务要自己拆 codedata.statusdata.success、HTTP 状态码, 才能判断「这笔钱到底收没收到」。现在库直接给结论。

三个新字段

发起支付 / 订单查询 / 取消订单的返回值上,多了三个可选字段:

interface IYeahPayResponse {
  // —— 原有字段,行为完全不变 ——
  success: boolean      // 仅表示 code === 0(网关受理),不表示支付成功
  code: string
  message: string
  data?: any
  traceId?: string
  isTestData?: boolean

  // —— v1.1.0 新增 ——
  outcome?: YeahPayOutcome    // 这笔支付当前收没收到钱
  reason?: YeahPayReason      // 归类后的原因,给界面文案 / i18n
  diagnostic?: string         // 原始定位信息,给日志
}

outcome —— 业务唯一需要看的字段

| 值 | 含义 | 业务该做什么 | |---|---|---| | 'paid' | 已收到钱 | 出货 / 打小票,终态 | | 'unpaid' | 确定没收到,且不会再收到 | 提示失败,终态 | | 'pending' | 还没定 | 继续轮询 queryOrder | | undefined | 本次操作不描述支付状态 | 见下方说明 |

const r = await client.queryOrder({ bizOrderId })
if (r.outcome === 'paid')   return onSuccess()
if (r.outcome === 'unpaid') return onFailed(r.reason)
// pending 或 undefined → 继续轮询

判断支付结果请用 outcome,不要用 success success 只代表网关受理了这次请求, 一笔被持卡人取消的支付同样会 success: false, code: '-1027' —— 但那是明确的「没收到钱」, 而网关 500 也是 success: false —— 那是「还不知道」。两者业务处理完全相反。

outcomeundefined 的两种情况

  1. 该操作描述的不是这笔支付 —— 退款、当日撤销、结算、重打印。 退款成功不等于原支付没收到钱,所以库不填。
  2. 遇到未知业务码 —— 库不猜。业务按 pending 处理(继续轮询)即可, 查询接口最终会给出确定结论。

注意:simulateCreateCharge / simulateQueryOrder 不产出 outcome。 如果开发模式依赖模拟接口,需自行处理,否则会一直轮询下去。

reason —— 给用户看的原因

type YeahPayReason =
  | 'cancelled_by_user'  | 'card_error'      | 'terminal_timeout' | 'issuer_declined'
  | 'order_closed'       | 'terminal_unbound'| 'terminal_offline' | 'invalid_request'
  | 'duplicate_order'    | 'auth_failed'     | 'rate_limited'     | 'gateway_error'
  | 'network_error'      | 'cancel_rejected' | 'order_not_found'

有限枚举,直接拿去做 i18n key。不要把 message 直接显示给用户 —— 那是网关原文, 中英混杂、措辞随版本变。

diagnostic —— 给日志看的原文

格式 <层>/<码>[#<子码>],一眼能看出问题出在哪一层:

| 样例 | 含义 | |---|---| | gateway/-1027 | 网关业务码 | | gateway/-1028#340 | 读卡失败,附终端子码(从 message 尾部 (340) 提取) | | http/502 | HTTP 层错误,网关业务逻辑没跑到 | | transport/ECONNREFUSED | 请求没出得去客户端 | | client/missing_authCode | 参数校验,请求根本没发 |

打日志时带上 diagnostictraceId,排查时可直接对上网关侧记录。

传输失败时

网络异常时库仍然抛错(与旧版一致),但会在 error 对象上挂一个 yeahpay 属性:

try {
  await client.createCardCharge({ amount, bizOrderId })
} catch (err: any) {
  const v = err.yeahpay   // { outcome, reason, diagnostic } | undefined
  if (v?.outcome === 'unpaid') return onFailed(v.reason)  // 确定没发出去
  // 否则订单可能已创建,必须去查,不能直接判失败
}

ECONNREFUSEDUnknownHostException 这类「请求从没离开客户端」的错误判 unpaid; 超时、连接中断判 pending —— 请求可能已经到达网关,订单可能已创建。


🔧 API 概览

class YeahPayClient {
  constructor(config: IYeahPayApiConfig)

  // 发起支付(填 outcome)
  createCardCharge(req: IYeahPayCreateChargeRequest): Promise<IYeahPayResponse>
  createSyncCardCharge(req: IYeahPayCreateChargeRequest): Promise<IYeahPayResponse>
  createQRScanCharge(req: IYeahPayCreateChargeRequest): Promise<IYeahPayResponse>   // 主扫,需 authCode
  createQRCodeCharge(req: IYeahPayCreateChargeRequest): Promise<IYeahPayResponse>   // 被扫,需 payWay

  // 查询(填 outcome)
  queryOrder(req: IYeahPayQueryRequest): Promise<IYeahPayResponse>
  queryOrderSync(req: IYeahPayQueryRequest): Promise<IYeahPayResponse>

  // 取消订单(填 outcome)
  cancelOrder(bizOrderId: string): Promise<IYeahPayResponse>

  // 以下不填 outcome —— 描述的不是原支付
  refundCardCharge(req: IYeahPayRefundRequest): Promise<IYeahPayResponse>
  cancelCardCharge(req: Omit<IYeahPayRefundRequest, 'amount'>): Promise<IYeahPayResponse>
  refundQRCharge(req: IYeahPayRefundRequest): Promise<IYeahPayResponse>
  settlement(bizSettlementFlowId: string): Promise<IYeahPayResponse>
  reprint(bizOrderId: string, bizRefundId?: string): Promise<IYeahPayResponse>

  // 模拟(不填 outcome)
  simulateCreateCharge(req: IYeahPaySimulateChargeRequest): Promise<IYeahPayResponse>
  simulateQueryOrder(req: IYeahPayQueryRequest): Promise<IYeahPayResponse>
}

判读逻辑本身也单独导出,便于业务对历史数据复判:

import { interpret } from '@posx/yeahpay'

⬆️ 从 1.0.x 升级到 1.1.0

业务代码零改动即可升级。 新增的三个字段都是可选的,原有六个字段的取值、 请求报文的组装(URL、header、加密体)与 1.0.18 逐字段一致。

升级后建议按下面改造,把手写的状态判断删掉:

// Before —— 业务自己拆协议
if (r.success && r.data?.status === 2) onSuccess()
else if (r.code === '-1027') onCancelled()
else if (r.data?.success === 'true') onSuccess()
else poll()

// After
if (r.outcome === 'paid')   onSuccess()
if (r.outcome === 'unpaid') onFailed(r.reason)
// 其余继续轮询

同时检查:配置里是否补了 syncUrl;日志里是否带上了 diagnostic


🧪 运行

npm test              # 单元测试
npm run test:integration
npm run build

📄 许可证

MIT(见 LICENSE


版本:v1.1.0