如何在 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曲线。
备注:内容仅供参考。
