- Blockchain By Example
- Bellaj Badr Richard Horrocks Xun (Brian) Wu
- 688字
- 2021-06-10 18:53:37
Sending a payment
The next function is the one to pay the bill. Before sending the bitcoins, this function will check the payment request details, including the merchant's x509 certificate. We print out in the console the payment request details to let the client know whom they are going to pay:
private static void send(PaymentSession session,WalletAppKit k) {
log.info("Payment Request");
log.info("Amount to Pay: " + session.getValue().toFriendlyString());
log.info("Date: " + session.getDate());
// Probably indicates what your are paying for.
log.info("Message from merchant : " + session.getMemo());
PaymentProtocol.PkiVerificationData identity = session.verifyPki();
if (identity != null) {
// Merchant identity from the certificate
log.info("Payment requester: " + identity.displayName);
// The issuing Certificate Authority
log.info("Certificate authority: " + identity.rootAuthorityName);
}
}
The important point in this first part is to validate the merchant's identity and signature using the PKI system. In fact, session.verifyPki() checks whether the merchant DER certificate containing the public key corresponding to the private key used to sign the PaymentRequest is signed by a trusted root authority. We display to the customer the merchant's identity and the certifying authority.
Then, we call the getSendRequest method to get the needed information about precisely how to send money to the merchant. Until now, the transaction in the request is incomplete; we need the client to confirm the payment transaction using completeTx(req), which adds outputs and signed inputs according to the instructions in the request. The client indicates a refund address and a short memo to the intended destination:
final SendRequest request = session.getSendRequest();
k.wallet().completeTx(request);
String customerMemo = "Nice Website";
Address refundAddress = new Address(params,"mfcjN5E6vp2NWpMvH7TM2xvTywzRtNvZWR"); ListenableFuture<PaymentProtocol.Ack> future =
session.sendPayment(ImmutableList.of(request.tx),refundAddress, customerMemo);
if (future != null) {
PaymentProtocol.Ack ack = future.get();
...
The client creates here a transaction that fully pays the PaymentRequest using completeTx. Then, we call the sendPayment method, which does not broadcast the transaction to the bitcoin network, but instead sends a Payment message after the customer has authorized payment and indicated a refund address.
More specifically, if payment_url is specified in the merchant's payment request, then the payment message is serialized and sent as the body of the POST request to that URL. The server will forward the payment transaction to the network.
Afterward the customer's wallet waits for an acknowledgment of payment from the server:
...
System.out.println("Memo from merchant :"+ack.getMemo());
...
Then, we put the given transaction into the wallet's pending pool:
...
kit.wallet().commitTx(request.tx);
...
At this level, we have to edit the server's code (server.js file) to make it able to broadcast the received transaction. For that, within the /payment route, we have to add a few lines broadcasting the raw transaction using the chain.so API as we did in first chapter:
...
var Rawtransaction = payment.get('transactions')[0].toBuffer();
var TransactionToBrodcast = new bitcore_lib.Transaction(Rawtransaction).toString('hex');
var ack = new PaymentProtocol().makePaymentACK();
ack.set('payment', payment.message);
console.log("the merchant brodcast")
var Sendingoptions = {
method: 'POST',
url: 'https://chain.so/api/v2/send_tx/BTCTEST',
body: { tx_hex: TransactionToBrodcast },
json: true };
rp(Sendingoptions).then(function (response) {
var Jresponse= JSON.stringify(response);
ack.set('memo', 'Payment processed,Thank you ;) \ninvoice ID :'+req.query.id+"\nTransaction Details : "+Jresponse );
var rawack = ack.serialize();
res.set({
'Content-Type': PaymentProtocol.PAYMENT_ACK_CONTENT_TYPE,
'Content-Length': rawack.length,
});
res.send(rawack);
});
...
The server should normally determine whether or not the transaction satisfies the conditions of payment after broadcasting the transaction. It should also wait for confirmations to make sure that the payment is received before returning an acknowledgment message, as a transaction could fail to confirm for many reasons.
However, if the bitcoin URI returned by the payment request doesn't contain a payment URL, we broadcast the signed transaction directly from the client side:
...
} else {
Wallet.SendResult sendResult = new Wallet.SendResult();
sendResult.tx = request.tx;
sendResult.broadcast = k.peerGroup().broadcastTransaction(request.tx);
sendResult.broadcastComplete = sendResult.broadcast.future();
}
And with this last function, we are all set to try out our simple BitcoinJ application. Make sure to properly shut down all the running services when you want to stop the kit:
log.info("stopping.."); kit.stopAsync(); kit.awaitTerminated();
Congratulations! You have finished your first BitcoinJ program and got it to run. The complete source code for this section is available on GitHub at https://github.com/bellaj/BitcoinJ_Bip70.