GET /donations/verification/verify/:transactionHashExample:
curl http://localhost:3000/donations/verification/verify/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855Response:
{
"isValid": true,
"transactionHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"amount": "100",
"asset": "XLM",
"projectId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2024-01-15T10:30:00Z",
"errors": []
}POST /donations/verification/verify
Authorization: Bearer <JWT_TOKEN>Request Body:
{
"transactionHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"expectedAmount": 100,
"expectedAsset": "XLM",
"expectedProjectId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Response: Same as above, but validates against expected values.
POST /donations/verification/record
Authorization: Bearer <ADMIN_JWT_TOKEN>Request Body:
{
"transactionHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"projectId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"donorId": "user-uuid-here", // Optional
"isAnonymous": false
}Success Response:
{
"success": true,
"donationId": "donation-uuid-here",
"message": "Donation recorded successfully",
"duplicate": false
}GET /donations/verification/check-duplicate/:transactionHashResponse:
{
"transactionHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"isDuplicate": true,
"exists": true
}GET /donations/verification/transaction/:transactionHashResponse (Found):
{
"success": true,
"donation": {
"id": "donation-uuid",
"projectId": "project-uuid",
"amount": 100,
"assetType": "XLM",
"transactionHash": "tx-hash",
"isAnonymous": false,
"verified": true,
"createdAt": "2024-01-15T10:30:00Z"
}
}Response (Not Found):
{
"success": false,
"message": "Donation not found",
"transactionHash": "tx-hash"
}{
"isValid": false,
"transactionHash": "invalid-hash",
"amount": "0",
"asset": "XLM",
"projectId": null,
"timestamp": "2024-01-15T10:30:00Z",
"errors": ["Invalid transaction hash format"]
}{
"isValid": false,
"transactionHash": "nonexistent-hash",
"amount": "0",
"asset": "XLM",
"projectId": null,
"timestamp": "2024-01-15T10:30:00Z",
"errors": ["Transaction not found on the Stellar blockchain"]
}{
"isValid": false,
"transactionHash": "tx-hash",
"amount": "95",
"asset": "XLM",
"projectId": "project-uuid",
"timestamp": "2024-01-15T10:30:00Z",
"errors": ["Amount mismatch: expected 100, got 95"]
}{
"isValid": false,
"transactionHash": "tx-hash",
"amount": "100",
"asset": "XLM",
"projectId": "project-uuid",
"timestamp": "2024-01-15T10:30:00Z",
"errors": ["Transaction destination is not a platform wallet"]
}User sends 100 XLM to platform wallet with memo: a1b2c3d4-e5f6-7890-abcd-ef1234567890
const response = await fetch(
'http://localhost:3000/donations/verification/verify/' + transactionHash
);
const result = await response.json();
if (result.isValid && result.projectId === expectedProjectId) {
// Transaction is valid, proceed to record
}const response = await fetch(
'http://localhost:3000/donations/verification/record',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${adminToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
transactionHash: result.transactionHash,
projectId: result.projectId,
donorId: currentUserId, // Optional
isAnonymous: false
})
}
);
const recordResult = await response.json();
console.log(`Donation recorded: ${recordResult.donationId}`);const projectResponse = await fetch(
'http://localhost:3000/projects/' + projectId
);
const project = await projectResponse.json();
console.log(`Funds raised: ${project.fundsRaised}`); // Should include donationThe existing DonationsService.create() already uses verification:
// In donations.service.ts
async create(createDonationDto: CreateDonationDto, donorId?: string) {
// Verify transaction on Stellar blockchain
const verificationResult =
await this.stellarBlockchainService.verifyTransaction(transactionHash);
if (!verificationResult.isValid) {
throw new BadRequestException(
`Transaction verification failed: ${verificationResult.error}`
);
}
// Create donation record
// ... rest of code ...
}For automated workflows (like Stellar sync polling):
// In stellar-sync-processor.service.ts
async processPaymentOperation(operation, transaction, projectId) {
// Verify transaction
const verification = await this.stellarBlockchainService.verifyTransaction(
transaction.id,
amount,
assetType,
operation.to,
);
if (!verification.isValid) {
this.logger.warn('Verification failed');
return;
}
// Record verified donation
const result = await this.donationRecordingService.recordVerifiedDonation({
isValid: verification.isValid,
transactionHash: transaction.id,
amount: amount.toString(),
asset: assetType,
projectId,
timestamp: new Date(transaction.created_at),
errors: [],
donorAddress: operation.from,
destinationAddress: operation.to,
});
console.log(`Donation recorded: ${result.donationId}`);
}Add to your .env file:
# REQUIRED: Platform wallet addresses to accept donations
STELLAR_PLATFORM_ADDRESSES=GD123...YOUR-PUBLIC-KEY-HERE
# Optional: Customize polling interval for Stellar sync
STELLAR_POLLING_INTERVAL_SECONDS=30
# Optional: Max retries for failed API calls
STELLAR_POLLING_MAX_RETRIES=3Visit: https://laboratory.stellar.org/#account-creator?network=test
Use Stellar Laboratory to send payment with memo:
- Network: Testnet
- Amount: 100 XLM
- Memo: Your project UUID
- Memo Type: TEXT
After submission, copy the transaction hash from the response.
curl http://localhost:3000/donations/verification/verify/YOUR_TX_HASH- Always verify before recording - Use the verification endpoint first
- Handle duplicates gracefully - Check before attempting to record
- Log all verification attempts - For audit trail
- Monitor error rates - Detect potential fraud or system issues
- Cache verification results - Already implemented (5-min TTL)
- Use admin endpoint for recording - Prevents unauthorized recordings
- Wait for Stellar network confirmation (~5 seconds)
- Check you're using correct network (testnet vs public)
- Verify transaction hash is complete (64 characters)
- Ensure
STELLAR_PLATFORM_ADDRESSESincludes your wallet - Check payment was sent to correct address
- Verify claimed amount matches actual blockchain amount
- Consider floating-point precision (tolerance: 0.000001)
- Ensure memo is in correct format (UUID or "donation:UUID")
- Check memo type is TEXT (not ID or HASH)
For issues or questions:
- Check logs for detailed error messages
- Review Stellar Horizon API response
- Verify environment configuration
- Test with Stellar Laboratory first