Skip to content

Commit c39afe5

Browse files
agent groups
1 parent 8efc09b commit c39afe5

File tree

2 files changed

+113
-76
lines changed

2 files changed

+113
-76
lines changed

src/iden3comm/handlers/payment.ts

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { PROTOCOL_MESSAGE_TYPE } from '../constants';
22
import { MediaType } from '../constants';
3-
import {
4-
BasicMessage,
5-
ContractInvokeTransactionData,
6-
IPackageManager,
7-
PackerParams
8-
} from '../types';
3+
import { BasicMessage, IPackageManager, PackerParams } from '../types';
94

105
import { DID } from '@iden3/js-iden3-core';
116
import * as uuid from 'uuid';
@@ -26,7 +21,7 @@ import { PaymentRequestDataType, PaymentRequestType, PaymentType } from '../../v
2621
* createPaymentRequest is a function to create protocol payment-request message
2722
* @param {DID} sender - sender did
2823
* @param {DID} receiver - receiver did
29-
* @param {PaymentRequestCreationOptions} opts - creation options
24+
* @param {PaymentRequestInfo[]} payments - payments
3025
* @returns `PaymentRequestMessage`
3126
*/
3227
export function createPaymentRequest(
@@ -93,30 +88,23 @@ export interface IPaymentHandler {
9388
* handle payment-request
9489
* @param {Uint8Array} request - raw byte message
9590
* @param {PaymentRequestMessageHandlerOptions} opts - handler options
96-
* @returns {Promise<Uint8Array>}` - payment message
91+
* @returns {Promise<Uint8Array>} - payment message
9792
*/
9893
handlePaymentRequest(
9994
request: Uint8Array,
10095
opts: PaymentRequestMessageHandlerOptions
10196
): Promise<Uint8Array>;
10297

10398
/**
104-
* @beta
105-
* handle payment protocol message
106-
* @param {PaymentMessage} payment - payment message
107-
* @param {PaymentHandlerOptions} opts - options
108-
* @returns `Promise<{
109-
payment: PaymentMessage;
110-
}>`
111-
*/
99+
* @beta
100+
* handle payment protocol message
101+
* @param {PaymentMessage} payment - payment message
102+
* @param {PaymentHandlerOptions} opts - options
103+
* @returns `Promise<void>`
104+
*/
112105
handlePayment(payment: PaymentMessage, opts: PaymentHandlerOptions): Promise<void>;
113106
}
114107

115-
/** @beta PaymentRequestMessageHandlerOptions represents payment-request handler options */
116-
export type PaymentTxData = ContractInvokeTransactionData & {
117-
amount: bigint;
118-
};
119-
120108
/** @beta PaymentRequestMessageHandlerOptions represents payment-request handler options */
121109
export type PaymentRequestMessageHandlerOptions = {
122110
txParams: unknown[];
@@ -161,14 +149,17 @@ export class PaymentHandler
161149

162150
public async handle(
163151
message: BasicMessage,
164-
context: PaymentRequestMessageHandlerOptions
152+
context: PaymentRequestMessageHandlerOptions | PaymentHandlerOptions
165153
): Promise<BasicMessage | null> {
166154
switch (message.type) {
167155
case PROTOCOL_MESSAGE_TYPE.PAYMENT_REQUEST_MESSAGE_TYPE:
168156
return (await this.handlePaymentRequestMessage(
169-
message as unknown as PaymentRequestMessage,
170-
context
157+
message as PaymentRequestMessage,
158+
context as PaymentRequestMessageHandlerOptions
171159
)) as BasicMessage;
160+
case PROTOCOL_MESSAGE_TYPE.PAYMENT_MESSAGE_TYPE:
161+
await this.handlePayment(message as PaymentMessage, context as PaymentHandlerOptions);
162+
return null;
172163
default:
173164
return super.handle(message, context as { [key: string]: unknown });
174165
}
@@ -189,7 +180,7 @@ export class PaymentHandler
189180
private async handlePaymentRequestMessage(
190181
paymentRequest: PaymentRequestMessage,
191182
ctx: PaymentRequestMessageHandlerOptions
192-
): Promise<PaymentMessage> {
183+
): Promise<BasicMessage> {
193184
if (!paymentRequest.to) {
194185
throw new Error(`failed request. empty 'to' field`);
195186
}
@@ -203,8 +194,9 @@ export class PaymentHandler
203194
}
204195

205196
const senderDID = DID.parse(paymentRequest.from);
197+
const receiverDID = DID.parse(paymentRequest.to);
206198

207-
const payments: PaymentInfo[] = [];
199+
const agentGroups: { agentURL: string; payments: PaymentInfo[] }[] = [];
208200
for (let i = 0; i < paymentRequest.body.payments.length; i++) {
209201
const paymentReq = paymentRequest.body.payments[i];
210202
if (paymentReq.type !== PaymentRequestType.PaymentRequest) {
@@ -216,16 +208,56 @@ export class PaymentHandler
216208
}
217209

218210
const txID = await ctx.paymentHandler(paymentReq.data, ctx.txParams);
219-
payments.push({
211+
const paymentInfo = {
220212
id: paymentReq.data.id,
221213
type: PaymentType.Iden3PaymentCryptoV1,
222214
paymentData: {
223215
txID
224216
}
225-
});
217+
};
218+
const paymentGroup = agentGroups.find((a) => a.agentURL === paymentReq.agent);
219+
if (paymentGroup) {
220+
paymentGroup.payments.push(paymentInfo);
221+
} else {
222+
agentGroups.push({ agentURL: paymentReq.agent, payments: [paymentInfo] });
223+
}
226224
}
227225

228-
return createPayment(senderDID, DID.parse(paymentRequest.from), payments);
226+
if (agentGroups.length > 1) {
227+
throw new Error(`all agent urls in payment objects should match`);
228+
}
229+
230+
const paymentMessage = createPayment(receiverDID, senderDID, agentGroups[0].payments);
231+
const responseEncoded = byteEncoder.encode(JSON.stringify(paymentMessage));
232+
const packerOpts =
233+
this._params.packerParams.mediaType === MediaType.SignedMessage
234+
? this._params.packerParams.packerOptions
235+
: {
236+
provingMethodAlg: proving.provingMethodGroth16AuthV2Instance.methodAlg
237+
};
238+
const response = await this._packerMgr.pack(
239+
this._params.packerParams.mediaType,
240+
responseEncoded,
241+
{
242+
senderDID,
243+
...packerOpts
244+
}
245+
);
246+
247+
const agentResult = await fetch(agentGroups[0].agentURL, {
248+
method: 'POST',
249+
body: response,
250+
headers: {
251+
'Content-Type': 'application/octet-stream'
252+
}
253+
});
254+
255+
const agentMessage = await agentResult.json();
256+
257+
if (!agentMessage) {
258+
throw new Error('empty response from agent');
259+
}
260+
return agentMessage as BasicMessage;
229261
}
230262

231263
/**
@@ -247,19 +279,17 @@ export class PaymentHandler
247279
throw new Error(`failed request. empty 'from' field`);
248280
}
249281

250-
const senderDID = DID.parse(paymentRequest.from);
251-
const message = await this.handlePaymentRequestMessage(paymentRequest, opts);
252-
const response = byteEncoder.encode(JSON.stringify(message));
282+
const agentMessage = await this.handlePaymentRequestMessage(paymentRequest, opts);
253283

284+
const response = byteEncoder.encode(JSON.stringify(agentMessage));
254285
const packerOpts =
255286
this._params.packerParams.mediaType === MediaType.SignedMessage
256287
? this._params.packerParams.packerOptions
257288
: {
258289
provingMethodAlg: proving.provingMethodGroth16AuthV2Instance.methodAlg
259290
};
260291

261-
// send to agent, return void
262-
292+
const senderDID = DID.parse(paymentRequest.from);
263293
return this._packerMgr.pack(this._params.packerParams.mediaType, response, {
264294
senderDID,
265295
...packerOpts

tests/handlers/payment.test.ts

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import {
1212
PackageManager,
1313
PaymentRequestType,
1414
PaymentRequestDataType,
15-
byteEncoder
15+
byteEncoder,
16+
PaymentType,
17+
BasicMessage,
18+
createProposal
1619
} from '../../src';
1720

1821
import {
@@ -29,24 +32,26 @@ import {
2932

3033
import { expect } from 'chai';
3134
import path from 'path';
32-
import { MediaType } from '../../src/iden3comm/constants';
35+
import { MediaType, PROTOCOL_MESSAGE_TYPE } from '../../src/iden3comm/constants';
3336
import { DID } from '@iden3/js-iden3-core';
3437
import {
38+
createPayment,
3539
createPaymentRequest,
3640
IPaymentHandler,
3741
PaymentHandler
3842
} from '../../src/iden3comm/handlers/payment';
3943
import {
40-
PaymentMessage,
4144
PaymentRequestDataInfo,
4245
PaymentRequestInfo
4346
} from '../../src/iden3comm/types/protocol/payment';
4447
import { Contract, ethers, JsonRpcProvider } from 'ethers';
48+
import fetchMock from '@gr2m/fetch-mock';
4549

4650
describe('payment-request handler', () => {
4751
let packageMgr: IPackageManager;
4852
let paymentHandler: IPaymentHandler;
4953
let userDID, issuerDID: DID;
54+
let agentMessageResponse: BasicMessage;
5055
const packageManager: IPackageManager = new PackageManager();
5156
packageManager.registerPackers([new PlainPacker()]);
5257

@@ -128,7 +133,7 @@ describe('payment-request handler', () => {
128133
chainID: 80002,
129134
address: '0x2C2007d72f533FfD409F0D9f515983e95bF14992'
130135
},
131-
agent: 'https://issuer.com',
136+
agent: 'https://agent-url.com',
132137
expiration: 2125558127,
133138
description: 'payment-request integration test'
134139
};
@@ -137,6 +142,10 @@ describe('payment-request handler', () => {
137142
return Promise.resolve('0x312312334');
138143
};
139144

145+
afterEach(() => {
146+
fetchMock.restore();
147+
});
148+
140149
beforeEach(async () => {
141150
const kms = registerKeyProvidersInMemoryKMS();
142151
const dataStorage = getInMemoryDataStorage(MOCK_STATE_STORAGE);
@@ -174,42 +183,43 @@ describe('payment-request handler', () => {
174183
});
175184

176185
issuerDID = issuerIdentity.did;
186+
187+
agentMessageResponse = createProposal(issuerDID, userDID, []);
188+
fetchMock.spy();
189+
fetchMock.post('https://agent-url.com', agentMessageResponse);
177190
});
178191

179192
it('payment-request handler test', async () => {
180-
const paymentRequest = createPaymentRequest(userDID, issuerDID, [paymentReqInfo]);
193+
const paymentRequest = createPaymentRequest(issuerDID, userDID, [paymentReqInfo]);
181194
const msgBytesRequest = await packageManager.pack(
182195
MediaType.PlainMessage,
183196
byteEncoder.encode(JSON.stringify(paymentRequest)),
184197
{}
185198
);
186-
const paymentMessageBytes = await paymentHandler.handlePaymentRequest(msgBytesRequest, {
199+
const agentMessageBytes = await paymentHandler.handlePaymentRequest(msgBytesRequest, {
187200
paymentHandler: paymentHandlerFuncMock,
188201
txParams: ['<session-id-hash>', '<issuer-did-hash>']
189202
});
190-
const { unpackedMessage: paymentMessage } = await packageManager.unpack(paymentMessageBytes);
203+
const { unpackedMessage: agentMessage } = await packageManager.unpack(agentMessageBytes);
191204

192-
expect((paymentMessage as PaymentMessage).body?.payments[0].id).to.be.eq(
193-
paymentReqInfo.data.id
205+
expect((agentMessage as BasicMessage).type).to.be.eq(
206+
PROTOCOL_MESSAGE_TYPE.PROPOSAL_MESSAGE_TYPE
194207
);
195-
196-
expect((paymentMessage as PaymentMessage).body?.payments[0].paymentData.txID).to.be.not.empty;
197208
});
198209

199210
it('payment handler', async () => {
200-
const paymentRequest = createPaymentRequest(userDID, issuerDID, [paymentReqInfo]);
201-
const msgBytesRequest = await packageManager.pack(
202-
MediaType.PlainMessage,
203-
byteEncoder.encode(JSON.stringify(paymentRequest)),
204-
{}
205-
);
206-
const paymentMessageBytes = await paymentHandler.handlePaymentRequest(msgBytesRequest, {
207-
paymentHandler: paymentHandlerFuncMock,
208-
txParams: ['<session-id-hash>', '<issuer-did-hash>']
209-
});
210-
const { unpackedMessage: paymentMessage } = await packageManager.unpack(paymentMessageBytes);
211+
const paymentRequest = createPaymentRequest(issuerDID, userDID, [paymentReqInfo]);
212+
const payment = createPayment(userDID, issuerDID, [
213+
{
214+
id: paymentRequest.body?.payments[0].data.id || 0,
215+
type: PaymentType.Iden3PaymentCryptoV1,
216+
paymentData: {
217+
txID: '0x312312334'
218+
}
219+
}
220+
]);
211221

212-
paymentHandler.handlePayment(paymentMessage as PaymentMessage, {
222+
await paymentHandler.handlePayment(payment, {
213223
paymentRequest,
214224
checkPaymentHandler: async () => {
215225
Promise.resolve();
@@ -218,39 +228,36 @@ describe('payment-request handler', () => {
218228
});
219229

220230
it.skip('payment-request handler (integration test)', async () => {
221-
const paymentRequest = createPaymentRequest(userDID, issuerDID, [paymentReqInfo]);
231+
const paymentRequest = createPaymentRequest(issuerDID, userDID, [paymentReqInfo]);
222232
const msgBytesRequest = await packageManager.pack(
223233
MediaType.PlainMessage,
224234
byteEncoder.encode(JSON.stringify(paymentRequest)),
225235
{}
226236
);
227-
const paymentMessageBytes = await paymentHandler.handlePaymentRequest(msgBytesRequest, {
237+
const agentMessageBytes = await paymentHandler.handlePaymentRequest(msgBytesRequest, {
228238
paymentHandler: paymentIntegrationHandlerFunc,
229239
txParams: ['<session-id-hash>', '<issuer-did-hash>']
230240
});
231-
const { unpackedMessage: paymentMessage } = await packageManager.unpack(paymentMessageBytes);
241+
const { unpackedMessage: agentMessage } = await packageManager.unpack(agentMessageBytes);
232242

233-
expect((paymentMessage as PaymentMessage).body?.payments[0].id).to.be.eq(
234-
paymentReqInfo.data.id
243+
expect((agentMessage as BasicMessage).type).to.be.eq(
244+
PROTOCOL_MESSAGE_TYPE.PROPOSAL_MESSAGE_TYPE
235245
);
236-
237-
expect((paymentMessage as PaymentMessage).body?.payments[0].paymentData.txID).to.be.not.empty;
238246
});
239247

240248
it.skip('payment handler (integration test)', async () => {
241-
const paymentRequest = createPaymentRequest(userDID, issuerDID, [paymentReqInfo]);
242-
const msgBytesRequest = await packageManager.pack(
243-
MediaType.PlainMessage,
244-
byteEncoder.encode(JSON.stringify(paymentRequest)),
245-
{}
246-
);
247-
const paymentMessageBytes = await paymentHandler.handlePaymentRequest(msgBytesRequest, {
248-
paymentHandler: paymentIntegrationHandlerFunc,
249-
txParams: ['<session-id-hash>', '<issuer-did-hash>']
250-
});
251-
const { unpackedMessage: paymentMessage } = await packageManager.unpack(paymentMessageBytes);
249+
const paymentRequest = createPaymentRequest(issuerDID, userDID, [paymentReqInfo]);
250+
const payment = createPayment(userDID, issuerDID, [
251+
{
252+
id: paymentRequest.body?.payments[0].data.id || 0,
253+
type: PaymentType.Iden3PaymentCryptoV1,
254+
paymentData: {
255+
txID: '0xe9bea8e7adfe1092a8a4ca2cd75f4d21cc54b9b7a31bd8374b558d11b58a6a1a'
256+
}
257+
}
258+
]);
252259

253-
paymentHandler.handlePayment(paymentMessage as PaymentMessage, {
260+
await paymentHandler.handlePayment(payment, {
254261
paymentRequest,
255262
checkPaymentHandler: paymentCheckIntegrationHandlerFunc
256263
});

0 commit comments

Comments
 (0)