Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
meta {
name: Continuation Request
type: http
seq: 8
}

post {
url: {{senderOpenPaymentsContinuationUri}}
body: json
auth: none
}

headers {
Authorization: GNAP {{continueToken}}
}

script:pre-request {
const scripts = require('./scripts');

await scripts.addSignatureHeaders();
}

script:post-response {
const scripts = require('./scripts');

scripts.storeTokenDetails();
}

tests {
test("Status code is 200", function() {
expect(res.getStatus()).to.equal(200);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
meta {
name: Get sender wallet address
type: http
seq: 1
}

get {
url: {{senderWalletAddress}}
body: none
auth: none
}

headers {
Accept: application/json
}

script:pre-request {
const scripts = require('./scripts');

scripts.addHostHeader("senderOpenPaymentsHost");
}

script:post-response {
const url = require('url')

if (res.getStatus() !== 200) {
return
}

const body = res.getBody()
bru.setEnvVar("senderAssetCode", body?.assetCode)
bru.setEnvVar("senderAssetScale", body?.assetScale)

const authUrl = url.parse(body?.authServer)
if (
authUrl.hostname.includes('cloud-nine-wallet') ||
authUrl.hostname.includes('happy-life-bank')
){
const port = authUrl.hostname.includes('cloud-nine-wallet')? authUrl.port: Number(authUrl.port) + 1000
bru.setEnvVar("senderOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port + authUrl.path);
} else {
bru.setEnvVar("senderOpenPaymentsAuthHost", body?.authServer);
}

const resourceUrl = url.parse(body?.resourceServer)
if (resourceUrl.hostname.includes('cloud-nine-wallet') || resourceUrl.hostname.includes('happy-life-bank')) {
const port = resourceUrl.hostname.includes('happy-life-bank') ? bru.getEnvVar('happyLifeOpenPaymentsPort') : bru.getEnvVar('cloudNineOpenPaymentsPort')
bru.setEnvVar("senderOpenPaymentsHost", 'http://localhost:' + port + resourceUrl.path);
} else {
bru.setEnvVar("senderOpenPaymentsHost", body?.resourceServer);
}
}

tests {
test("Status code is 200", function() {
expect(res.getStatus()).to.equal(200);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
meta {
name: Grant Request for Subject Information
type: http
seq: 7
}

post {
url: {{senderOpenPaymentsAuthHost}}
body: json
auth: none
}

body:json {
{
"subject": {
"sub_ids": [
{
"id": "{{senderWalletAddress}}",
"format": "uri"
}
]
},
"client": "{{clientWalletAddress}}",
"interact": {
"start": [
"redirect"
]
}
}

}

script:pre-request {
const scripts = require('./scripts');

await scripts.addSignatureHeaders();
}

script:post-response {
const scripts = require('./scripts');

scripts.storeTokenDetails();

const body = res.getBody()
bru.setEnvVar("senderOpenPaymentsContinuationUri", body?.continue.uri)
}

tests {
test("Status code is 200", function() {
expect(res.getStatus()).to.equal(200);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
meta {
name: Vailidating Wallet Address Ownership with Open Payments
seq: 6
}
7 changes: 6 additions & 1 deletion localenv/mock-account-servicing-entity/app/lib/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ export class ApiClient {
if (response.status === 200) {
return {
isFailure: false,
payload: response.data.access,
payload: {
access: response.data.access,
subject: response.data.subject,
grantId: response.data.grantId,
state: response.data.state
},
contextUpdates: {
grant: response.data
}
Expand Down
5 changes: 5 additions & 0 deletions localenv/mock-account-servicing-entity/app/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export interface Access {
limits?: AccessLimit
}

export interface SubjectId {
id: string
format: string
}

export type InstanceConfig = {
name: string
logo: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Dispatch, SetStateAction } from 'react'
import { useEffect, useState } from 'react'
import { Button } from '~/components'
import { ApiClient } from '~/lib/apiClient'
import type { Access, InstanceConfig } from '~/lib/types'
import type { Access, InstanceConfig, SubjectId } from '~/lib/types'
import { CONFIG } from '~/lib/parse_config.server'

interface ConsentScreenContext {
Expand All @@ -20,6 +20,7 @@ interface ConsentScreenContext {
returnUrl: string
accesses: Array<Access> | null
outgoingPaymentAccess: Access | null
subjectId: SubjectId | null
price: GrantAmount | null
costToUser: GrantAmount | null
errors: Array<Error>
Expand Down Expand Up @@ -47,19 +48,23 @@ export function loader() {
function ConsentScreenBody({
_thirdPartyUri,
thirdPartyName,
accesses,
price,
costToUser,
interactId,
nonce,
returnUrl
returnUrl,
subjectId
}: {
_thirdPartyUri: string
thirdPartyName: string
accesses: Access[] | null
price: GrantAmount | null
costToUser: GrantAmount | null
interactId: string
nonce: string
returnUrl: string
subjectId: SubjectId | null
}) {
const chooseConsent = (accept: boolean) => {
const href = new URL(returnUrl)
Expand All @@ -72,31 +77,46 @@ function ConsentScreenBody({
return (
<>
<div className='bg-white rounded-md p-8 px-16'>
<div className='col-12'>
{price && (
<p>
{thirdPartyName} wants to send {price.currencyDisplayCode}{' '}
{price.amount.toFixed(2)} to its account.
</p>
)}
<div className='row mt-2'>
<div className='col-12'>
{subjectId && (
<p>
{thirdPartyName} is asking you to confirm ownership of{' '}
{subjectId.id}.
</p>
)}
</div>
</div>
<div className='row mt-2'>
<div className='col-12'>
{price && (
<p>
{thirdPartyName} wants to send {price.currencyDisplayCode}{' '}
{price.amount.toFixed(2)} to its account.
</p>
)}
</div>
</div>
<div className='row mt-2'>
<div className='col-12'>
{costToUser && (
<p>
This will cost you {costToUser.currencyDisplayCode}{' '}
You will be charged {costToUser.currencyDisplayCode}{' '}
{costToUser.amount.toFixed(2)}
</p>
)}
</div>
</div>
<div className='row mt-2'>
<div className='col-12'>
{!price && !costToUser && (
{accesses?.length &&
accesses.length > 0 &&
!price &&
!costToUser ? (
<p>
{thirdPartyName} is requesting grant for an unlimited amount
</p>
)}
) : undefined}
</div>
</div>
<div className='row mt-2'>
Expand Down Expand Up @@ -238,6 +258,7 @@ export default function ConsentScreen({ idpSecretParam }: ConsentScreenProps) {
//TODO returnUrl: 'http://localhost:3030/mock-idp/consent?interactid=demo-interact-id&nonce=demo-interact-nonce',
accesses: null,
outgoingPaymentAccess: null,
subjectId: null,
price: null,
costToUser: null,
errors: new Array<Error>()
Expand Down Expand Up @@ -292,21 +313,21 @@ export default function ConsentScreen({ idpSecretParam }: ConsentScreenProps) {
...ctx,
errors: response.errors.map((e) => new Error(e))
})
} else if (!response.payload) {
} else if (!response.payload.access && !response.payload.subject) {
setCtx({
...ctx,
errors: [new Error('no accesses in grant')]
errors: [new Error('no accesses or subjects in grant')]
})
} else {
const outgoingPaymentAccess =
response.payload.find(
response.payload.access.find(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(p: Record<string, any>) => p.type === 'outgoing-payment'
) || null
const returnUrlObject = new URL(ctx.returnUrl)
returnUrlObject.searchParams.append(
'grantId',
outgoingPaymentAccess.grantId
response.payload.grantId
)
returnUrlObject.searchParams.append(
'thirdPartyName',
Expand Down Expand Up @@ -334,17 +355,24 @@ export default function ConsentScreen({ idpSecretParam }: ConsentScreenProps) {
outgoingPaymentAccess?.limits?.receiveAmount?.assetScale ??
null
)
if (outgoingPaymentAccess) {
returnUrlObject.searchParams.append(
'amountType',
outgoingPaymentAccess.limits?.receiveAmount
? AmountType.RECEIVE
: outgoingPaymentAccess.limits?.debitAmount
? AmountType.DEBIT
: AmountType.UNLIMITED
)
}
returnUrlObject.searchParams.append(
'amountType',
outgoingPaymentAccess?.limits?.receiveAmount
? AmountType.RECEIVE
: outgoingPaymentAccess?.limits?.debitAmount
? AmountType.DEBIT
: AmountType.UNLIMITED
'subjectId',
response.payload.subject.sub_ids[0]?.id ?? null
)
setCtx({
...ctx,
accesses: response.payload,
accesses: response.payload.access,
subjectId: response.payload.subject.sub_ids[0],
outgoingPaymentAccess: outgoingPaymentAccess,
thirdPartyName: ctx.thirdPartyName,
thirdPartyUri: ctx.thirdPartyUri,
Expand Down Expand Up @@ -433,11 +461,13 @@ export default function ConsentScreen({ idpSecretParam }: ConsentScreenProps) {
<ConsentScreenBody
_thirdPartyUri={ctx.thirdPartyUri}
thirdPartyName={ctx.thirdPartyName}
accesses={ctx.accesses}
price={ctx.price}
costToUser={ctx.costToUser}
interactId={ctx.interactId}
nonce={ctx.nonce}
returnUrl={ctx.returnUrl}
subjectId={ctx.subjectId}
/>
)}
</>
Expand Down
Loading
Loading