PAYATHON 2026

如何在 Java 中使用 BitPay 并配置 private key?

支付小周

结论

private key 不需要在 createInvoice() 中临时生成。它应该是一个长期保存的 EC 私钥,使用 secp256k1 曲线生成。使用 BitPay 客户端前,还要用这把私钥完成配对,取得具有 pos 权限的 API token。

你当前的代码每次调用都会执行:

ECKey key = KeyUtils.createEcKey();

这行代码每次都会生成新的密钥。新密钥没有配对记录,也没有创建 Invoice 所需的 token,不能直接用于已授权的客户端。

正确的处理方式是:首次启动时生成并安全保存密钥,完成一次配对;此后始终加载同一把密钥及其对应的 token。

首次生成并保存 private key

如果当前 SDK 使用 bitcoinj 的 ECKey,可以把 32 字节私钥编码为十六进制后保存。下面只演示密钥生成,省略具体的安全存储实现。生产环境中不要把私钥直接写进源码。

import org.bitcoinj.core.ECKey;

public final class BitPayKeyGenerator {

    public static String generatePrivateKeyHex() {
        ECKey key = new ECKey();
        return key.getPrivateKeyAsHex();
    }
}

这段代码只需执行一次。生成的返回值应保存到密钥管理系统、环境变量或权限严格受控的配置文件中,也不要在日志里输出生产私钥。

从配置加载 private key

假设通过环境变量 BITPAY_PRIVATE_KEY 提供私钥:

import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;

public final class BitPayKeyLoader {

    public static ECKey loadFromEnvironment() {
        String privateKeyHex = System.getenv("BITPAY_PRIVATE_KEY");

        if (privateKeyHex == null || privateKeyHex.isBlank()) {
            throw new IllegalStateException(
                "Environment variable BITPAY_PRIVATE_KEY is not configured"
            );
        }

        byte[] privateKeyBytes = Utils.HEX.decode(privateKeyHex.trim());
        return ECKey.fromPrivate(privateKeyBytes);
    }
}

部分旧版 bitcoinj 没有 ECKey.fromPrivate(byte[])。这类版本通常可以改用:

import java.math.BigInteger;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;

byte[] privateKeyBytes = Utils.HEX.decode(privateKeyHex);
ECKey key = new ECKey(new BigInteger(1, privateKeyBytes), null);

具体采用哪种写法,取决于项目实际使用的 bitcoinj 版本。

完成 BitPay 配对

只有私钥还不够。你必须使用同一把私钥向 BitPay 请求配对,取得创建 Invoice 所需的 token。

旧版 Java client 的调用方式通常类似下面这样:

ECKey key = BitPayKeyLoader.loadFromEnvironment();
BitPay bitpay = new BitPay(key);

String pairingCode = bitpay.requestClientAuthorization("pos");
System.out.println("Pairing code: " + pairingCode);

接下来,需要在对应的 BitPay 商户后台确认这个 pairing code。配对成功后,SDK 会取得或使用与该私钥身份绑定的 pos token。

不同版本的 BitPay Java SDK 在配对方法、token 保存方式和构造函数上有所不同。有些版本要求显式调用授权方法,有些则要求在配置文件中提供 token。因此,上面的代码只适用于与题目代码风格一致的旧版 SDK。实际方法签名应以项目安装版本的 API 为准。

创建 Invoice

配对完成后,业务代码应加载已经保存的私钥,而不是每次重新生成:

public void createInvoice() throws BitPayException {
    ECKey key = BitPayKeyLoader.loadFromEnvironment();
    BitPay bitpay = new BitPay(key);

    InvoiceBuyer buyer = new InvoiceBuyer();
    buyer.setName("Satoshi");
    buyer.setEmail("satoshi@bitpay.com");

    Invoice invoice = new Invoice(100.0, "USD");
    invoice.setBuyer(buyer);
    invoice.setFullNotifications(true);
    invoice.setNotificationEmail("satoshi@bitpay.com");
    invoice.setPosData("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");

    Invoice createdInvoice = bitpay.createInvoice(invoice);

    // 按需要使用 createdInvoice,例如读取 Invoice ID 或支付 URL。
}

需要修改的是这一行:

ECKey key = BitPayKeyLoader.loadFromEnvironment();

不要再在每次创建 Invoice 时调用:

ECKey key = KeyUtils.createEcKey();

更适合生产环境的结构

生产环境中,可以在应用启动时初始化一次 BitPay 客户端:

public final class BitPayClientProvider {

    private final BitPay bitPay;

    public BitPayClientProvider() throws BitPayException {
        ECKey key = BitPayKeyLoader.loadFromEnvironment();
        this.bitPay = new BitPay(key);
    }

    public BitPay getClient() {
        return bitPay;
    }
}

业务服务直接复用这个客户端:

public Invoice createInvoice(BitPay bitpay) throws BitPayException {
    InvoiceBuyer buyer = new InvoiceBuyer();
    buyer.setName("Satoshi");
    buyer.setEmail("satoshi@bitpay.com");

    Invoice invoice = new Invoice(100.0, "USD");
    invoice.setBuyer(buyer);
    invoice.setFullNotifications(true);
    invoice.setNotificationEmail("satoshi@bitpay.com");
    invoice.setPosData("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");

    return bitpay.createInvoice(invoice);
}

注意事项

  • private key 和 API token 是两种不同的凭据。私钥用于标识客户端身份和签名请求,token 用于控制 API 权限。
  • 加载的私钥必须与最初配对时使用的密钥完全一致。私钥丢失后,通常需要重新生成密钥并再次配对。
  • 不要将私钥提交到 Git,也不要把它硬编码到 Java 源码、Dockerfile 或公开配置文件中。
  • 测试环境和生产环境应分别使用不同的私钥与 token,不能混用。
  • posData 不适合存放密码、私钥或其他敏感信息。
  • 不同代际的 BitPay Java SDK 在配置方式上差异较大。较新的版本可能通过配置文件中的 PrivateKeyPath、环境配置和 token 映射加载凭据,不再直接使用 new BitPay(ECKey)。如果当前依赖支持这类配置机制,应优先采用对应版本的配置模型。
  • 不要自行生成普通 RSA 密钥或使用任意 EC 曲线密钥。题目中的旧版客户端使用 ECKey,对应 Bitcoin 生态常见的 secp256k1 曲线。

备注:内容仅供参考。