Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions examples/4-minimal-with-webhook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
"author": "",
"license": "ISC",
"dependencies": {
"@code-wallet/client": "^1.3.1",
"@code-wallet/client": "^2.0.7",
"@code-wallet/library": "^1.3.3",
"body-parser": "^1.20.2",
"ejs": "^3.1.9",
"express": "^4.18.2"
"express": "^4.18.2",
"jose": "^5.2.0"
},
"devDependencies": {
"@types/express": "^4.17.17"
Expand Down
70 changes: 50 additions & 20 deletions examples/4-minimal-with-webhook/server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as code from "@code-wallet/client";
import express from "express";
import bodyParser from "body-parser";
import { jwtVerify, importJWK } from 'jose';
import { PublicKey } from "@code-wallet/library";
import * as code from "@code-wallet/client";

const port = 3030;
const app = express();
Expand All @@ -21,26 +23,31 @@ app.get('/', function(req, res) {
app.post('/create-intent', async (req, res) => {

const { clientSecret, id } = await code.paymentIntents.create({
amount: 0.01,
amount: 0.05,
currency: 'usd',
destination: 'E8otxw1CVX9bfyddKu3ZB3BVLa4VVF9J7CTPdnUwT9jR',

// The location where we want to be notified once the payment is made. This
// URL must be publicly accessible and live at the time this call is made.
//
// You can use cloudflare tunnels to test this locally:
// See https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/install-and-setup/tunnel-guide/local/
//
// For example, run `cloudflared tunnel --url localhost:3030`
// 1) Then replace the url below with the url provided by cloudflare tunnels
// 2) And finally, start the server with `node server.js`
webhook: { url: 'https://example.com/webhook' },
});

// The id value can also be used to query the status of the payment intent
// manually. See examples/3-minimal-with-verify/server.js
console.log('Created intent', id);

// The location where we want to be notified once the payment is made. This
// URL must be publicly accessible and live at the time this call is made.
//
// You can use cloudflare tunnels to test this locally:
// See https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/install-and-setup/tunnel-guide/local/
//
// For example, run `cloudflared tunnel --url localhost:3030`
// 1) Then replace the url below with the url provided by cloudflare tunnels
// 2) And finally, start the server with `node server.js`
const { success, message } = await code.webhook.register({
intent: id,
url: 'https://example.com/webhook',
})

console.log('Registered webhook', success, message);

// The clientSecret value needs to be sent to the browser so that the browser
// can use it to setup a payment with this intent instance. The client will
// use the payment details along with this value to derive the same payment
Expand All @@ -51,20 +58,15 @@ app.post('/create-intent', async (req, res) => {
// This endpoint is where Code will let us know that a payment has been made.
// You can use this to gate content or update your database that a payment has
// been made.
app.post('/webhook', function(req, res) {
app.post('/webhook', async (req, res) => {

console.log('Received webhook event');

// Once we receive a webhook event, we can use the JWT to verify that it was
// sent by Code and that it hasn't been tampered with. It will be signed with
// the public key `codeHy87wGD5oMRLG75qKqsSi1vWE3oxNyYmXo5F9YR`

const jwt = req.body.split('.');
const [header, payload, signature] = jwt.map((part) => Buffer.from(part, 'base64'));

console.log('header:\n', header.toString());
console.log('payload:\n', payload.toString());
console.log('signature:\n', signature.toString('hex'));
const jwt = req.body;

// Example response:
// header:
Expand All @@ -74,6 +76,12 @@ app.post('/webhook', function(req, res) {
// signature:
// 0d3e5361e13a727ca7cb0aaed9bb30652266d24701aae7ce26c3acc6f1886274310f69af3ca545f3057fc628ec9c45e74b606da7d44539f05637a6c8c56cde01

// Verify the JWT
const publicKey = 'codeHy87wGD5oMRLG75qKqsSi1vWE3oxNyYmXo5F9YR';
const event = await verifyToken(jwt, publicKey);

console.log('Verified webhook event', event);

// After verifying the JWT, you can check the payload to see if the payment
// was successful.

Expand All @@ -82,6 +90,28 @@ app.post('/webhook', function(req, res) {
res.send({ success: true });
});

/**
* A helper function to verify a token using the public key.
* @throws {Error} If the token is invalid or has been tampered with.
*/
async function verifyToken(token, publicKey) {
const publicKeyBuffer = PublicKey.fromBase58(publicKey).toBuffer();
const publicKeyBase64Url = publicKeyBuffer.toString('base64url');

const jwk = {
kty: 'OKP',
crv: 'Ed25519',
alg: 'EdDSA',
x: publicKeyBase64Url,
};

// Import the JWK to a format that the 'jose' library can use
const importedKey = await importJWK(jwk, 'EdDSA');

// Verify the token
return await jwtVerify(token, importedKey, { algorithms: ['EdDSA'] });
}

app.listen(port, () => {
console.log(`🚀 Minimal example listening on port ${port}`)
console.log(`http://localhost:${port}`)
Expand Down
Loading