Dodo Payment 服务

Dodo Payment 服务

把一次性 Dodo Payments Checkout 同步成用户全局余额充值。

PaymentService 通过 dodoPaymentProvider() 创建 Dodo Payments Checkout,并在 Dodo 通过 webhook 确认支付成功后,完成一笔已经存在的 balance topup。

这个服务只负责 Checkout 创建、本地支付记录、webhook 事件记录,以及最后调用 balance.finishTopup() 入账。

启用方式

import { Federation } from "@downcity/city";
import {
  BalanceService,
  PaymentService,
  dodoPaymentProvider,
} from "@downcity/services";

const base = new Federation({ db });
const balance = new BalanceService();

base.use(balance);
base.use(new PaymentService({
  // 关键说明(中文):PaymentService 直接接收 readTopup / finishTopup 两个函数,
  // 不再需要 balance bridge 对象包一层。
  readTopup: async (topup_id) => await balance.readTopup(topup_id),
  finishTopup: async (topup_id, extra) => await balance.finishTopup(topup_id, extra),
  providers: [
      dodoPaymentProvider({
        api_key: process.env.DODO_PAYMENTS_API_KEY,
        product_id: process.env.DODO_PRODUCT_ID,
        webhook_key: process.env.DODO_WEBHOOK_KEY,
      }),
  ],
}));

必需 env

  • DODO_PAYMENTS_API_KEYdodopayments SDK 使用的 API key
  • DODO_PRODUCT_ID:Dodo Checkout 使用的 product
  • DODO_WEBHOOK_KEY:可选,用于校验 webhook 签名
  • DODO_ENVIRONMENT:可选,test_modelive_mode,默认 test_mode
  • DOWNCITY_CITY_BASE_URL:可选,City 对外访问地址,用于生成内置结果页
  • DODO_CURRENCY:可选,支付方式目录展示用币种
  • DODO_API_BASE_URL:可选,测试环境 API 地址覆写

Dodo 的 return / cancel URL 会自动基于 DOWNCITY_CITY_BASE_URL 生成;未配置时会回退到当前请求 origin,方便本地开发。

前端流程

先通过 balance 创建可信 topup,再为它创建 Dodo checkout:

const checkout = await user.service("payment").action("checkout/create").invoke({
  method_id: "dodo",
  topup_id: "topup_demo",
});

location.href = checkout.checkout_url;

PaymentService() 也会在支付方式目录里暴露 Dodo:

const methods = await guest.service("payment").get("methods");

只有同时配置 DODO_PAYMENTS_API_KEYDODO_PRODUCT_ID 时,Dodo method 才会是 enabled。

Webhook 同步

把 Dodo webhook 事件发到:

POST /v1/payment/webhook

服务会基于事件 ID 幂等应用 payment.succeeded。重复事件只返回已有同步状态,不会重复给余额入账。

路由

  • GET /v1/payment/methods
  • POST /v1/payment/checkout/create
  • GET /v1/payment/payments/me
  • GET /v1/payment/payments
  • GET /v1/payment/events
  • POST /v1/payment/webhook
  • GET /v1/payment/redirect/success
  • GET /v1/payment/redirect/cancel