diff --git a/examples/4-minimal-with-webhook/package.json b/examples/4-minimal-with-webhook/package.json index 07f2573..734a0d1 100644 --- a/examples/4-minimal-with-webhook/package.json +++ b/examples/4-minimal-with-webhook/package.json @@ -5,7 +5,7 @@ "main": "index.js", "type": "module", "scripts": { - "start": "node index.js", + "start": "node server.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", diff --git a/examples/4-minimal-with-webhook/views/index.ejs b/examples/4-minimal-with-webhook/views/index.ejs index 1d0678f..bc3f6f7 100644 --- a/examples/4-minimal-with-webhook/views/index.ejs +++ b/examples/4-minimal-with-webhook/views/index.ejs @@ -4,7 +4,7 @@ - Code SDK - Example/3-minimal-with-webhook + Code SDK - Example/4-minimal-with-webhook diff --git a/examples/6-minimal-with-login/package.json b/examples/6-minimal-with-login/package.json new file mode 100644 index 0000000..cff700f --- /dev/null +++ b/examples/6-minimal-with-login/package.json @@ -0,0 +1,22 @@ +{ + "name": "6-minimal-with-login", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node server.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@code-wallet/client": "^1.1.0", + "body-parser": "^1.20.2", + "ejs": "^3.1.9", + "express": "^4.18.2" + }, + "devDependencies": { + "@types/express": "^4.17.17" + } +} diff --git a/examples/6-minimal-with-login/server.js b/examples/6-minimal-with-login/server.js new file mode 100644 index 0000000..fddce0a --- /dev/null +++ b/examples/6-minimal-with-login/server.js @@ -0,0 +1,70 @@ +import * as code from "@code-wallet/client"; +import { Keypair } from "@code-wallet/library"; +import express from "express"; + +const port = process.env.PORT || 3000; +const hostname = process.env.HOSTNAME || 'example.com'; +const app = express(); +const verifier = Keypair.generate(); + +// Set the view engine to ejs +app.set('view engine', 'ejs'); + +// index page (with the payment button) +app.get('/', function(req, res) { + res.render('index'); +}); + +// This is a page that the user will be redirected to once the payment is made. +app.get('/success/:id', async (req, res) => { + // Get the payment intent id from the URL + const intent = req.params.id; + + // Get the status of the payment intent and the user's public key + const status = await code.paymentIntents.getStatus({ intent }); + + // Render the success page with the intent id and status + res.render('success', { intent, status }); +}); + +// This is a JSON file that Code will look for when verifying your domain. It +// should be publicly accessible at the root of your domain. For example: +// https://example.com/.well-known/code-payments.json +app.get('/.well-known/code-payments.json', (req, res) => { + res.json({ "public_keys": [verifier.getPublicKey().toBase58()] }); +}); + +// Create a payment intent. We're letting Code know that a payment is coming +// soon and we want to be notified once it's made. +app.post('/create-intent', async (req, res) => { + + const { clientSecret, id } = await code.paymentIntents.create({ + amount: 0.05, + currency: 'usd', + destination: 'E8otxw1CVX9bfyddKu3ZB3BVLa4VVF9J7CTPdnUwT9jR', + + login: { + verifier: verifier.getPublicKey().toBase58(), + + // Cannot be localhost or a subdomain. It must be a domain that you own + // and have access to. Code will verify that this domain is owned by you + // by looking for the .well-known/code-payments.json file. + domain: hostname, + }, + + signers: [ verifier ] + }); + + console.log('Created intent', id); + + // 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 + // intent id on its end. + res.send({ clientSecret }); +}); + +app.listen(port, () => { + console.log(`🚀 Minimal example listening on port ${port}`) + console.log(`http://localhost:${port}`) +}); diff --git a/examples/6-minimal-with-login/views/index.ejs b/examples/6-minimal-with-login/views/index.ejs new file mode 100644 index 0000000..cc60371 --- /dev/null +++ b/examples/6-minimal-with-login/views/index.ejs @@ -0,0 +1,46 @@ + + + + + + + Code SDK - Example/6-minimal-with-login + + + +
+ + + + + \ No newline at end of file diff --git a/examples/6-minimal-with-login/views/success.ejs b/examples/6-minimal-with-login/views/success.ejs new file mode 100644 index 0000000..a163b0f --- /dev/null +++ b/examples/6-minimal-with-login/views/success.ejs @@ -0,0 +1,17 @@ + + + + + + + + Code SDK - Example/6-minimal-with-login + + + +

Payment Submitted

+
Intent Id: <%= intent %>
+
Status:
<%= status %>
+ + + \ No newline at end of file diff --git a/examples/package-lock.json b/examples/package-lock.json index 9ef36fd..d73dd86 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -15,10 +15,11 @@ } }, "../packages/client": { - "version": "1.0.1", + "name": "@code-wallet/client", + "version": "1.1.0", "license": "MIT", "dependencies": { - "@code-wallet/library": "^1.0.0", + "@code-wallet/library": "^1.1.0", "bs58": "^5.0.0", "buffer": "6.0.3" }, @@ -59,7 +60,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@code-wallet/client": "file:../../packages/client", + "@code-wallet/client": "^1.0.0", "body-parser": "^1.20.2", "ejs": "^3.1.9", "express": "^4.18.2" @@ -72,7 +73,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@code-wallet/client": "file:../../packages/client", + "@code-wallet/client": "^1.0.0", "body-parser": "^1.20.2", "ejs": "^3.1.9", "express": "^4.18.2" @@ -108,6 +109,19 @@ "ts-node-dev": "^2.0.0" } }, + "6-minimal-with-login": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@code-wallet/client": "^1.1.0", + "body-parser": "^1.20.2", + "ejs": "^3.1.9", + "express": "^4.18.2" + }, + "devDependencies": { + "@types/express": "^4.17.17" + } + }, "node_modules/@babel/parser": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", @@ -534,6 +548,10 @@ "resolved": "5-example-purchase-flow", "link": true }, + "node_modules/6-minimal-with-login": { + "resolved": "6-minimal-with-login", + "link": true + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2084,7 +2102,7 @@ "@code-wallet/client": { "version": "file:../packages/client", "requires": { - "@code-wallet/library": "^1.0.0", + "@code-wallet/library": "^1.1.0", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "@types/node": "^20.5.7", @@ -2450,7 +2468,7 @@ "3-minimal-with-verify": { "version": "file:3-minimal-with-verify", "requires": { - "@code-wallet/client": "file:../../packages/client", + "@code-wallet/client": "^1.0.0", "@types/express": "^4.17.17", "body-parser": "^1.20.2", "ejs": "^3.1.9", @@ -2460,7 +2478,7 @@ "4-minimal-with-webhook": { "version": "file:4-minimal-with-webhook", "requires": { - "@code-wallet/client": "file:../../packages/client", + "@code-wallet/client": "^1.0.0", "@types/express": "^4.17.17", "body-parser": "^1.20.2", "ejs": "^3.1.9", @@ -2491,6 +2509,16 @@ "vue": "^3.3.4" } }, + "6-minimal-with-login": { + "version": "file:6-minimal-with-login", + "requires": { + "@code-wallet/client": "^1.1.0", + "@types/express": "^4.17.17", + "body-parser": "^1.20.2", + "ejs": "^3.1.9", + "express": "^4.18.2" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", diff --git a/package-lock.json b/package-lock.json index dc1e010..a01e1e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3903,10 +3903,10 @@ }, "packages/client": { "name": "@code-wallet/client", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "dependencies": { - "@code-wallet/library": "^1.1.0", + "@code-wallet/library": "^1.1.1", "bs58": "^5.0.0", "buffer": "6.0.3" }, @@ -3972,7 +3972,7 @@ }, "packages/library": { "name": "@code-wallet/library", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "dependencies": { "@code-wallet/rpc": "^1.1.0", @@ -4098,7 +4098,7 @@ "@code-wallet/client": { "version": "file:packages/client", "requires": { - "@code-wallet/library": "^1.1.0", + "@code-wallet/library": "^1.1.1", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "@types/node": "^20.5.7", diff --git a/packages/client/package.json b/packages/client/package.json index 92862e8..47d01dd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@code-wallet/client", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "repository": { "type": "git", @@ -22,7 +22,7 @@ "maintained node versions" ], "dependencies": { - "@code-wallet/library": "^1.1.0", + "@code-wallet/library": "^1.1.1", "bs58": "^5.0.0", "buffer": "6.0.3" }, diff --git a/packages/library/package.json b/packages/library/package.json index 0ed16f3..430bc16 100644 --- a/packages/library/package.json +++ b/packages/library/package.json @@ -1,6 +1,6 @@ { "name": "@code-wallet/library", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "repository": { "type": "git", diff --git a/packages/library/src/elements/validate.ts b/packages/library/src/elements/validate.ts index 3f05f01..6b8db94 100644 --- a/packages/library/src/elements/validate.ts +++ b/packages/library/src/elements/validate.ts @@ -13,7 +13,7 @@ import { ErrNotImplemented, ErrInvalidValue, } from '../errors'; -import { Keypair, PublicKey } from '../keys'; +import { PublicKey } from '../keys'; /** * Validates the properties of the given `ElementOptions` for intents. @@ -96,7 +96,7 @@ function validateSigners(intent: ElementOptions) { if (!intent.signers) { return; } for (const signer of intent.signers) { - if (!(signer instanceof Keypair)) { + if (!signer.sign && typeof signer.sign !== 'function') { throw ErrInvalidValue(); } } diff --git a/packages/library/src/intents/PaymentRequestWithLoginIntent.ts b/packages/library/src/intents/PaymentRequestWithLoginIntent.ts index 539703a..51c4e53 100644 --- a/packages/library/src/intents/PaymentRequestWithLoginIntent.ts +++ b/packages/library/src/intents/PaymentRequestWithLoginIntent.ts @@ -102,17 +102,17 @@ class PaymentRequestWithLoginIntent extends PaymentRequestIntent { throw ErrLoginVerifierRequired(); } - const msg = this.toProto(); - const req = msg.kind.value as proto.RequestToReceiveBill; - if (!req) { + const envelope = this.toProto(); + const msg = envelope.kind.value as proto.RequestToReceiveBill; + if (!msg) { throw ErrUnexpectedError(); } - req.signature = new proto.Common.Signature({ - value: this.signer.sign(req.toBinary()), + msg.signature = new proto.Common.Signature({ + value: this.signer.sign(msg.toBinary()), }); - const sig = this.rendezvousKeypair.sign(msg.toBinary()); + const sig = this.rendezvousKeypair.sign(envelope.toBinary()); const intent = this.rendezvousKeypair.getPublicKey().toBase58(); const message = msg.toBinary(); const signature = sig; diff --git a/packages/library/test/payment_request_with_login.test.ts b/packages/library/test/payment_request_with_login.test.ts index eebe9f4..4a1883c 100644 --- a/packages/library/test/payment_request_with_login.test.ts +++ b/packages/library/test/payment_request_with_login.test.ts @@ -183,30 +183,28 @@ describe('PaymentRequestWithLoginIntent', () => { intent.rendezvousKeypair = rendezvous; const res = intent.sign(); - const msg = proto.Message.fromBinary(res.message); + const msg = proto.RequestToReceiveBill.fromBinary(res.message); expect(msg.toJson()).to.deep.equal({ - requestToReceiveBill: { - requestorAccount: { - value: PublicKey.fromBase58(destination).toBuffer().toString('base64') - }, - partial: { - currency: 'usd', - nativeAmount: 0.5, - }, - domain: { - value: domain - }, - verifier: { - value: verifier.getPublicKey().toBuffer().toString('base64') - }, - rendezvousKey: { - value: rendezvous.getPublicKey().toBuffer().toString('base64') - }, - signature: { - value: '7EdpP8TSajVJ+79X0yCmG5FAlIlpJEO8Qsvo4CuSwyOLsJMXMqb15TrVyrFiNINEYHWfxpzJvwNkvrdiZT74Dg==' - }, - } + requestorAccount: { + value: PublicKey.fromBase58(destination).toBuffer().toString('base64') + }, + partial: { + currency: 'usd', + nativeAmount: 0.5, + }, + domain: { + value: domain + }, + verifier: { + value: verifier.getPublicKey().toBuffer().toString('base64') + }, + rendezvousKey: { + value: rendezvous.getPublicKey().toBuffer().toString('base64') + }, + signature: { + value: '7EdpP8TSajVJ+79X0yCmG5FAlIlpJEO8Qsvo4CuSwyOLsJMXMqb15TrVyrFiNINEYHWfxpzJvwNkvrdiZT74Dg==' + }, }); }); @@ -223,32 +221,32 @@ describe('PaymentRequestWithLoginIntent', () => { intent.rendezvousKeypair = rendezvous; const res = intent.sign(); - const msg = proto.Message.fromBinary(res.message); + const msg = proto.RequestToReceiveBill.fromBinary(res.message); const actual = msg.toBinary(); const expected = new Uint8Array([ - 0x2a, 0xd3, 0x01, 0x0a, 0x22, 0x0a, 0x20, 0xab, 0x88, 0x67, - 0x2f, 0x94, 0x4e, 0xa4, 0x5b, 0x3c, 0x25, 0xc2, 0x6d, 0x73, - 0x2d, 0x2e, 0x5e, 0x40, 0xd5, 0xc7, 0xc1, 0x62, 0xc3, 0xcd, - 0x68, 0x58, 0xd5, 0xc9, 0x5a, 0x23, 0xfa, 0x34, 0x55, 0x1a, - 0x0e, 0x0a, 0x03, 0x75, 0x73, 0x64, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x22, 0x11, 0x0a, 0x0f, 0x61, - 0x70, 0x70, 0x2e, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x64, 0x65, - 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x22, 0x0a, 0x20, 0x90, 0x5c, - 0xc7, 0x96, 0xae, 0x7a, 0x19, 0x98, 0x46, 0x18, 0x36, 0xcd, - 0x9f, 0x59, 0x05, 0x2f, 0x8a, 0x2a, 0x52, 0xcd, 0x53, 0x9b, - 0x41, 0x7e, 0x57, 0x7c, 0x11, 0x82, 0x83, 0xd2, 0xa0, 0x6c, - 0x32, 0x42, 0x0a, 0x40, 0xec, 0x47, 0x69, 0x3f, 0xc4, 0xd2, - 0x6a, 0x35, 0x49, 0xfb, 0xbf, 0x57, 0xd3, 0x20, 0xa6, 0x1b, - 0x91, 0x40, 0x94, 0x89, 0x69, 0x24, 0x43, 0xbc, 0x42, 0xcb, - 0xe8, 0xe0, 0x2b, 0x92, 0xc3, 0x23, 0x8b, 0xb0, 0x93, 0x17, - 0x32, 0xa6, 0xf5, 0xe5, 0x3a, 0xd5, 0xca, 0xb1, 0x62, 0x34, - 0x83, 0x44, 0x60, 0x75, 0x9f, 0xc6, 0x9c, 0xc9, 0xbf, 0x03, - 0x64, 0xbe, 0xb7, 0x62, 0x65, 0x3e, 0xf8, 0x0e, 0x3a, 0x22, - 0x0a, 0x20, 0x70, 0xa4, 0xae, 0xb6, 0x8f, 0x76, 0x36, 0x3f, - 0x22, 0x66, 0x81, 0xdf, 0x16, 0x62, 0xbf, 0xc0, 0xf6, 0x42, - 0x36, 0xe9, 0x7a, 0x7a, 0x64, 0x87, 0x46, 0x6a, 0x93, 0x99, - 0x54, 0x5e, 0x7a, 0xfb + 0x0a, 0x22, 0x0a, 0x20, 0xab, 0x88, 0x67, 0x2f, 0x94, 0x4e, + 0xa4, 0x5b, 0x3c, 0x25, 0xc2, 0x6d, 0x73, 0x2d, 0x2e, 0x5e, + 0x40, 0xd5, 0xc7, 0xc1, 0x62, 0xc3, 0xcd, 0x68, 0x58, 0xd5, + 0xc9, 0x5a, 0x23, 0xfa, 0x34, 0x55, 0x1a, 0x0e, 0x0a, 0x03, + 0x75, 0x73, 0x64, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x3f, 0x22, 0x11, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x2e, + 0x67, 0x65, 0x74, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2a, 0x22, 0x0a, 0x20, 0x90, 0x5c, 0xc7, 0x96, 0xae, + 0x7a, 0x19, 0x98, 0x46, 0x18, 0x36, 0xcd, 0x9f, 0x59, 0x05, + 0x2f, 0x8a, 0x2a, 0x52, 0xcd, 0x53, 0x9b, 0x41, 0x7e, 0x57, + 0x7c, 0x11, 0x82, 0x83, 0xd2, 0xa0, 0x6c, 0x32, 0x42, 0x0a, + 0x40, 0xec, 0x47, 0x69, 0x3f, 0xc4, 0xd2, 0x6a, 0x35, 0x49, + 0xfb, 0xbf, 0x57, 0xd3, 0x20, 0xa6, 0x1b, 0x91, 0x40, 0x94, + 0x89, 0x69, 0x24, 0x43, 0xbc, 0x42, 0xcb, 0xe8, 0xe0, 0x2b, + 0x92, 0xc3, 0x23, 0x8b, 0xb0, 0x93, 0x17, 0x32, 0xa6, 0xf5, + 0xe5, 0x3a, 0xd5, 0xca, 0xb1, 0x62, 0x34, 0x83, 0x44, 0x60, + 0x75, 0x9f, 0xc6, 0x9c, 0xc9, 0xbf, 0x03, 0x64, 0xbe, 0xb7, + 0x62, 0x65, 0x3e, 0xf8, 0x0e, 0x3a, 0x22, 0x0a, 0x20, 0x70, + 0xa4, 0xae, 0xb6, 0x8f, 0x76, 0x36, 0x3f, 0x22, 0x66, 0x81, + 0xdf, 0x16, 0x62, 0xbf, 0xc0, 0xf6, 0x42, 0x36, 0xe9, 0x7a, + 0x7a, 0x64, 0x87, 0x46, 0x6a, 0x93, 0x99, 0x54, 0x5e, 0x7a, + 0xfb ]); expect(actual.toString()).to.equal(expected.toString());