Skip to content

Commit 946eec9

Browse files
authored
feat: Edge API integration improvements (#3534)
* feat: Edge API integration improvements - Add statusUrl to payment-info responses (buy/sell/swap) - Add pagination (limit/offset) to GET /transaction - Add stateProvince parameter to quote endpoints - Add expiryDate to payment-info responses (30 min validity) - Add userCountry to TransactionDto - Document amount units in Swagger and README * fix: format
1 parent 6c269af commit 946eec9

14 files changed

Lines changed: 81 additions & 6 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ Links to the productive API are used in the further documentation.
1818
- [Swagger UI](https://dev.api.dfx.swiss)
1919
- [Swagger JSON](https://dev.api.dfx.swiss/swagger-json)
2020

21+
### API Conventions
22+
23+
**Amount Representation:** All amount fields in the API use human-readable display units. For example, `1.5` means 1.5 BTC (not 150,000,000 satoshis). Crypto amounts are typically rounded to ~5 decimal places, fiat amounts to 2 decimal places.
24+
2125
## On-/Off-Ramp
2226

2327
This section explains the key concepts for using the DFX on-ramp and off-ramp.

src/config/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export class Configuration {
5050
defaultWalletId = 1;
5151
transactionRefundExpirySeconds = 300; // 5 minutes - enough time to fill out the refund form
5252
txRequestWaitingExpiryDays = 7;
53+
txRequestValidityMinutes = 30;
5354
financeLogTotalBalanceChangeLimit = 5000;
5455
faucetAmount = 20; //CHF
5556
faucetEnabled = process.env.FAUCET_ENABLED === 'true';

src/main.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { NestFactory } from '@nestjs/core';
33
import { WsAdapter } from '@nestjs/platform-ws';
44
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
55
import * as AppInsights from 'applicationinsights';
6-
import { useContainer } from 'class-validator';
76
import { spawnSync } from 'child_process';
7+
import { useContainer } from 'class-validator';
88
import cors from 'cors';
99
import { json, raw, text } from 'express';
1010
import helmet from 'helmet';
@@ -77,7 +77,10 @@ async function bootstrap() {
7777

7878
const swaggerOptions = new DocumentBuilder()
7979
.setTitle('DFX API')
80-
.setDescription(`DFX API ${Config.environment.toUpperCase()} (updated on ${new Date().toLocaleString()})`)
80+
.setDescription(
81+
`DFX API ${Config.environment.toUpperCase()} (updated on ${new Date().toLocaleString()})\n\n` +
82+
'**Amount Convention:** All amount fields use human-readable display units (e.g., 1.5 BTC, not 150,000,000 satoshis). ',
83+
)
8184
.setExternalDoc('Github documentation', Config.social.github)
8285
.setVersion(Config.defaultVersionString)
8386
.addBearerAuth()

src/subdomains/core/buy-crypto/routes/buy/dto/buy-payment-info.dto.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ export class BuyPaymentInfoDto extends BankInfoDto {
4545
@ApiProperty({ description: 'UID of the transaction order' })
4646
uid?: string;
4747

48+
@ApiPropertyOptional({ description: 'URL to the order status page' })
49+
statusUrl?: string;
50+
4851
@ApiProperty({ description: 'Price timestamp' })
4952
timestamp: Date;
5053

@@ -121,4 +124,7 @@ export class BuyPaymentInfoDto extends BankInfoDto {
121124

122125
@ApiPropertyOptional({ description: 'Whether this uses a personal IBAN' })
123126
isPersonalIban?: boolean;
127+
128+
@ApiPropertyOptional({ description: 'Expiration timestamp of the quote' })
129+
expiryDate?: Date;
124130
}

src/subdomains/core/buy-crypto/routes/buy/dto/get-buy-quote.dto.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,9 @@ export class GetBuyQuoteDto {
6969
@IsOptional()
7070
@IsString()
7171
country?: string;
72+
73+
@ApiPropertyOptional({ description: 'State or province code (e.g. US-NY, CA-BC)' })
74+
@IsOptional()
75+
@IsString()
76+
stateProvince?: string;
7277
}

src/subdomains/core/buy-crypto/routes/swap/dto/swap-payment-info.dto.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ export class SwapPaymentInfoDto {
1515
@ApiProperty({ description: 'UID of the transaction order' })
1616
uid?: string;
1717

18+
@ApiPropertyOptional({ description: 'URL to the order status page' })
19+
statusUrl?: string;
20+
1821
@ApiProperty({ description: 'Price timestamp' })
1922
timestamp: Date;
2023

@@ -103,4 +106,7 @@ export class SwapPaymentInfoDto {
103106
description: 'Whether gasless transaction is available for this request',
104107
})
105108
gaslessAvailable?: boolean;
109+
110+
@ApiPropertyOptional({ description: 'Expiration timestamp of the quote' })
111+
expiryDate?: Date;
106112
}

src/subdomains/core/history/dto/history-query.dto.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
22
import { Type } from 'class-transformer';
3-
import { IsDate, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
3+
import { IsDate, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, Max, Min } from 'class-validator';
44
import { Blockchain } from 'src/integration/blockchain/shared/enums/blockchain.enum';
55
import { ExportType } from '../services/history.service';
66
import { HistoryFilter } from './history-filter.dto';
@@ -32,6 +32,21 @@ export class HistoryQuery extends HistoryFilter {
3232
@IsOptional()
3333
@IsString()
3434
blockchains?: string;
35+
36+
@ApiPropertyOptional({ description: 'Maximum number of transactions to return', default: 1000 })
37+
@IsOptional()
38+
@IsNumber()
39+
@Min(1)
40+
@Max(10000)
41+
@Type(() => Number)
42+
limit?: number;
43+
44+
@ApiPropertyOptional({ description: 'Number of transactions to skip', default: 0 })
45+
@IsOptional()
46+
@IsNumber()
47+
@Min(0)
48+
@Type(() => Number)
49+
offset?: number;
3550
}
3651

3752
export class HistoryQueryUser extends HistoryQuery {

src/subdomains/core/history/mappers/transaction-dto.mapper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export class TransactionDtoMapper {
111111
asset: buyCrypto.networkStartAsset,
112112
}
113113
: null,
114+
userCountry: buyCrypto.transaction.userData?.country?.symbol,
114115
};
115116

116117
return Object.assign(new TransactionDto(), dto);
@@ -181,6 +182,7 @@ export class TransactionDtoMapper {
181182
chargebackDate: buyFiat.chargebackDate,
182183
date: buyFiat.transaction.created,
183184
externalTransactionId: buyFiat.transaction.externalId,
185+
userCountry: buyFiat.transaction.userData?.country?.symbol,
184186
};
185187

186188
return Object.assign(new TransactionDto(), dto);
@@ -241,6 +243,7 @@ export class TransactionDtoMapper {
241243
date: txRequest.created,
242244
externalTransactionId: null,
243245
networkStartTx: null,
246+
userCountry: txRequest.user?.userData?.country?.symbol,
244247
};
245248

246249
return Object.assign(new TransactionDto(), dto);
@@ -303,6 +306,7 @@ export class TransactionDtoMapper {
303306
chargebackTxUrl: undefined,
304307
chargebackDate: undefined,
305308
date: refReward.transaction.created,
309+
userCountry: refReward.transaction.userData?.country?.symbol,
306310
};
307311

308312
return Object.assign(new TransactionDto(), dto);

src/subdomains/core/history/services/history.service.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,14 @@ export class HistoryService {
122122
): Promise<{ buyCryptos: BuyCrypto[]; buyFiats: BuyFiat[]; refRewards: RefReward[] }> {
123123
const transactions =
124124
user instanceof UserData
125-
? await this.transactionService.getTransactionsForAccount(user.id, query.from, query.to)
126-
: await this.transactionService.getTransactionsForUsers([user.id], query.from, query.to);
125+
? await this.transactionService.getTransactionsForAccount(
126+
user.id,
127+
query.from,
128+
query.to,
129+
query.limit,
130+
query.offset,
131+
)
132+
: await this.transactionService.getTransactionsForUsers([user.id], query.from, query.to, query.limit);
127133

128134
const all =
129135
query.buy == null && query.sell == null && query.staking == null && query.ref == null && query.lm == null;

src/subdomains/core/sell-crypto/route/dto/get-sell-quote.dto.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,9 @@ export class GetSellQuoteDto {
6262
@IsOptional()
6363
@IsString()
6464
country?: string;
65+
66+
@ApiPropertyOptional({ description: 'State or province code (e.g. US-NY, CA-BC)' })
67+
@IsOptional()
68+
@IsString()
69+
stateProvince?: string;
6570
}

0 commit comments

Comments
 (0)