Product Requirements Document (PRD)
Trust My Group - Hands In Integration
Document Version: 1.0
Date: October 6, 2025
Owner: Hands In Integration Team
Reviewers: Engineering, Business Development, Customer Success
Executive Summary
Trust My Group requires integration with Hands In's payment orchestration platform to enable their customers to use Hands In's flexible payment solutions (multi-card and group payments) within Trust My Group's existing checkout flow. This integration will connect through Hands In's existing SDK, API, and webhook systems while maintaining Trust My Group's current ACI payment processing infrastructure.
Key Integration Points
- API Integration: Create payment sessions via Hands In's multi-card and group payment APIs
- SDK Integration: Hands In payment widgets embedded in Trust My Group's checkout
- Webhook Integration: Real-time payment event notifications from Hands In to Trust My Group
- ACI Webhook Filtering: Modify Trust My Group's ACI webhook handler to ignore Hands In payments
1. Project Overview
1.1 Business Objectives
- Enable Trust My Group customers to access Hands In's flexible payment options (multi-card split payments, group payments)
- Maintain Trust My Group's existing ACI payment processing relationship and workflow
- Provide seamless checkout experience with minimal disruption to current booking flow
- Enable real-time payment status synchronization between systems
1.2 Success Metrics
-
Technical KPIs:
- Payment session creation latency < 500ms
- Webhook delivery success rate > 99.5%
- SDK load time < 2 seconds
- Zero false positive bookings from ACI webhook filtering
-
Business KPIs:
- Integration go-live within 8 weeks
- Zero payment processing downtime during integration
- Customer satisfaction score maintained or improved
- Successful identification and filtering of Hands In payments in ACI webhooks
2. Technical Requirements
2.1 Trust My Group Implementation Requirements
2.1.1 Hands In API Integration
Status: New Development Required by Trust My Group
Required API Endpoints to Integrate:
-
Multi-Card Payment Creation:
POST /multi-card -
Group Payment Creation:
POST /group-payments
API Integration Requirements:
- API Key authentication with Hands In
- Create payment sessions for customer checkout
- Handle payment metadata for booking association
- Error handling and retry logic for API failures
- Support for both multi-card and group payment flows
2.1.2 Hands In SDK Integration
Status: Implementation Required by Trust My Group
SDK Features Available:
- Multi-card payment widget (
renderMultiCard) - Group payment widget (
renderGroupPayment) - Responsive design for mobile/desktop
- Real-time payment status updates
- Event handling for payment completion
Integration Methods:
// Load Hands In SDK
<script src="https://cdn.handsin.com/v1/sdk.js"></script>
// Multi-Card Widget Integration
window.handsin({
merchantId: 'trust_my_group_merchant_id',
environment: 'production'
}).renderMultiCard('payment-container', {
live: true,
merchantId: 'trust_my_group',
multiCardId: 'mc_session_123',
style: { maxWidth: '600px' },
multiCardDefaultEnabled: true
});
2.1.3 Webhook Receiver Implementation
Status: New Development Required by Trust My Group
Required Webhook Endpoint: POST /webhooks/handsin
Webhook Events to Handle:
MULTI_CARD_COMPLETED- Multi-card payment completedMULTI_CARD_CANCELLED- Multi-card payment cancelledMULTI_CARD_EXPIRED- Multi-card payment expiredGROUP_PAYMENT_COMPLETED- Group payment completedGROUP_PAYMENT_CANCELLED- Group payment cancelledGROUP_PAYMENT_EXPIRED- Group payment expiredREFUND_COMPLETED- Payment refunded
Webhook Payload Structure (Based on EventData interface):
{
"id": "evt_abc123",
"eventType": "MULTI_CARD_COMPLETED",
"merchantId": "trust_my_group",
"createdAt": "2025-10-06T14:30:00Z",
"multiCardId": "mc_payment_123",
"data": {
"id": "mc_payment_123",
"status": "COMPLETED",
"amountMoney": {
"amount": 15000,
"currency": "USD"
},
"metaData": {
"orderId": "tmg_order_789",
"customerId": "customer_123",
"createdBy": "trust_my_group"
}
}
}
2.2 Trust My Group Requirements
2.2.1 ACI Webhook Filtering
Identification Methods:
- Primary: Check
metadata.createdBy === 'handsin'in ACI webhook payload - Fallback: Statement descriptor patterns (if available)
- Transaction reference patterns (if configured)
Implementation:
// Modified ACI webhook handler
function handleACIWebhook(webhookPayload) {
// Check if payment was created by Hands In
if (webhookPayload.metadata?.createdBy === 'handsin') {
// Skip processing - this will be handled by Hands In webhooks
console.log('Skipping Hands In originated payment:', webhookPayload.id);
return;
}
// Process normal ACI payments for booking creation
processACIEvent(webhookPayload);
}
2.2.2 Multi-Card Payment Creation
API Endpoint: POST https://api.handsin.com/v1/multi-card
Request Example:
const createMultiCardPayment = async (orderData) => {
const response = await fetch('https://api.handsin.com/v1/multi-card', {
method: 'POST',
headers: {
'x-api-key': ' ' + process.env.HANDSIN_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
idempotencyKey: `tmg_${orderData.id}_${Date.now()}`,
amountMoney: {
amount: orderData.totalAmount, // in cents
currency: 'USD'
},
customerId: orderData.customerId,
lineItemParams: orderData.items.map(item => ({
item: {
name: item.name,
amountMoney: { amount: item.price, currency: 'USD' }
},
quantity: item.quantity
})),
metaData: {
orderId: orderData.id,
customerId: orderData.customerId,
createdBy: 'trust_my_group'
},
pageOptions: {
successUrl: `https://trustmygroup.com/booking-success/${orderData.id}`,
cancelUrl: `https://trustmygroup.com/checkout/${orderData.id}`,
maxNumberCards: 4
}
})
});
return response.json();
};
2.2.3 Group Payment Creation
API Endpoint: POST https://api.handsin.com/v1/group-payments
Request Example:
const createGroupPayment = async (orderData, invitees) => {
const response = await fetch('https://api.handsin.com/v1/group-payments', {
method: 'POST',
headers: {
'x-api-key': process.env.HANDSIN_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
idempotencyKey: `tmg_group_${orderData.id}_${Date.now()}`,
amountMoney: {
amount: orderData.totalAmount,
currency: 'USD'
},
customerId: orderData.ownerId,
invites: invitees.map(invitee => ({
firstName: invitee.firstName,
email: invitee.email
})),
splitType: 'EQUAL',
splitAllocation: invitees.length + 1, // +1 for owner
metaData: {
orderId: orderData.id,
ownerId: orderData.ownerId,
createdBy: 'trust_my_group'
},
pageOptions: {
successUrl: `https://trustmygroup.com/booking-success/${orderData.id}`,
cancelUrl: `https://trustmygroup.com/checkout/${orderData.id}`
},
expirationDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days
})
});
return response.json();
};
2.2.4 Frontend Checkout Integration
Checkout Page Modifications:
<!-- Hands In SDK -->
<script src="https://cdn.handsin.com/js/sdk.js"></script>
<!-- Enhanced Payment Options -->
<div class="payment-methods">
<input type="radio" name="payment" value="standard" checked>
<label>Standard Payment</label>
<input type="radio" name="payment" value="multicard">
<label>Split Payment (Multiple Cards)</label>
</div>
<!-- Payment Method Containers -->
<div id="multicard-container" style="display: none;"></div>
JavaScript Integration:
async function initializeMultiCardWidget() {
try {
// Create multi-card payment session
const multiCardData = await createMultiCardPayment(currentOrder);
// Render SDK widget
window.handsin({
merchantId: 'trust_my_group',
environment: 'production'
}).renderMultiCard('#multicard-container', {
live: true,
merchantId: 'trust_my_group',
multiCardId: multiCardData.id,
style: { maxWidth: '100%' },
multiCardDefaultEnabled: true,
});
} catch (error) {
showErrorMessage('Failed to initialize multi-card payment');
}
}
2.2.5 Webhook Receiver Implementation
New Endpoint Required: POST /webhooks/handsin
const crypto = require('crypto');
app.post('/webhooks/handsin', express.json(), async (req, res) => {
try {
const signature = req.headers['x-handsin-signature'];
// Verify webhook signature
if (!verifyHandsInWebhookSignature(req.body, signature)) {
return res.status(401).send('Unauthorized');
}
const { eventType, createdAt, data } = req.body;
switch (eventType) {
case 'MULTI_CARD_COMPLETED':
await handleMultiCardCompleted(data);
break;
case 'GROUP_PAYMENT_COMPLETED':
await handleGroupPaymentCompleted(data);
break;
case 'MULTI_CARD_CANCELLED':
case 'MULTI_CARD_EXPIRED':
case 'GROUP_PAYMENT_CANCELLED':
case 'GROUP_PAYMENT_EXPIRED':
await handlePaymentFailed(data);
break;
case 'REFUND_COMPLETED':
await handlePaymentRefunded(data);
break;
default:
console.log(`Unhandled webhook event: ${eventType}`);
}
res.status(200).send('OK');
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).send('Internal Server Error');
}
});
function verifyHandsInWebhookSignature(body, signature) {
const expectedSignature = crypto
.createHmac('sha256', process.env.HANDSIN_WEBHOOK_SECRET)
.update(JSON.stringify(body))
.digest('hex');
return signature === expectedSignature;
}
async function handleMultiCardCompleted(data) {
// Create booking when multi-card payment is completed
}
async function handleGroupPaymentCompleted(data) {
// Create booking when group payment is completed
}
async function handlePaymentFailed(data) {
// Update order status to failed
}
3. Data Flow Architecture
3.1 Payment Flow Sequence
sequenceDiagram
participant Customer
participant TMG as Trust My Group
participant HI as Hands In API
participant ACI as ACI Gateway
participant TMG_WH as TMG Webhooks
Customer->>TMG: Select Hands In Payment
TMG->>HI: Create Payment Session
HI->>TMG: Return Session Data
TMG->>Customer: Render Hands In Widget
Customer->>HI: Submit Payment
HI->>ACI: Process Payment
ACI->>HI: Payment Result
HI->>TMG_WH: Send Webhook (GROUP_PAYMENT_COMPLETED)
TMG_WH->>TMG: Create Booking
TMG->>Customer: Booking Confirmation
3.2 Webhook Event Flow
graph LR
ACI[ACI Gateway] --> ACIWH[ACI Webhook Handler]
ACIWH --> Filter{HandsIn Payment?}
Filter -->|Yes| Skip[Skip Processing]
Filter -->|No| Process[Process Booking]
HI[Hands In] --> HIWH[Hands In Webhook]
HIWH --> Verify[Verify Signature]
Verify --> Booking[Create Booking]
4. Implementation Plan
4.1 Phase 1: Foundation Setup
Trust My Group Deliverables:
- Hands In merchant account setup and API key generation
- Configure ACI connection in Hands In dashboard
- Development environment configuration
- ACI webhook handler modification for filtering payments created by Hands In
4.2 Phase 2: Core Integration
Trust My Group Deliverables:
- Multi-card payment API integration
- Group payment API integration
- Frontend checkout UI modifications
- Hands In SDK integration
- Hands In Webhook handler implementation
4.3 Phase 3: Testing & Production
Trust My Group Deliverables:
- End-to-end testing in staging environment
- User acceptance testing with internal team
5. Technical Specifications
5.1 API Specifications
5.1.1 Multi-Card Payment Creation
POST https://api.handsin.com/v1/multi-card
Content-Type: application/json
x-api-key: {api_key}
{
"idempotencyKey": "tmg_order_123_1728234567",
"amountMoney": {
"amount": 25000,
"currency": "USD"
},
"customerId": "tmg_customer_456",
"metaData": {
"orderId": "tmg_order_123",
"customerId": "tmg_customer_456",
"createdBy": "trust_my_group"
},
"pageOptions": {
"successUrl": "https://trustmygroup.com/success/123",
"cancelUrl": "https://trustmygroup.com/checkout/123",
"maxNumberCards": 4
}
}
Response:
{
"id": "mc_abc123",
"status": "PENDING",
"url": "https://checkout.handsin.com/m/mc_abc123",
"amountMoney": {
"amount": 25000,
"currency": "USD"
}
}
5.1.2 Group Payment Creation
POST https://api.handsin.com/v1/group-payments
Content-Type: application/json
x-api-key: {api_key}
{
"idempotencyKey": "tmg_group_123_1728234567",
"amountMoney": {
"amount": 50000,
"currency": "USD"
},
"customerId": "tmg_customer_456",
"invites": [
{ "firstName": "John", "email": "john@example.com" },
{ "firstName": "Jane", "email": "jane@example.com" }
],
"splitType": "EQUAL",
"splitAllocation": 3,
"metaData": {
"orderId": "tmg_order_123",
"ownerId": "tmg_customer_456",
"createdBy": "trust_my_group"
},
"expirationDate": "2025-10-13T14:30:00Z"
}
Response:
{
"id": "gp_def456",
"status": "PENDING",
"url": "https://checkout.handsin.com/g/gp_def456",
"amountMoney": {
"amount": 50000,
"currency": "USD"
}
}
5.2 Webhook Specifications
5.2.1 Signature Verification
x-handsin-signature: calculated_hmac_signature
HMAC-SHA256 signature calculated from:
JSON.stringify(request_body) using webhook_secret
5.2.2 Event Types
MULTI_CARD_CREATED- Multi-card payment createdMULTI_CARD_UPDATED- Multi-card payment updatedMULTI_CARD_APPROVED- Multi-card payment approvedMULTI_CARD_COMPLETED- Multi-card payment completedMULTI_CARD_CANCELLED- Multi-card payment cancelledMULTI_CARD_EXPIRED- Multi-card payment expiredGROUP_PAYMENT_CREATED- Group payment createdGROUP_PAYMENT_UPDATED- Group payment updatedGROUP_PAYMENT_APPROVED- Group payment approvedGROUP_PAYMENT_COMPLETED- Group payment completedGROUP_PAYMENT_CANCELLED- Group payment cancelledGROUP_PAYMENT_EXPIRED- Group payment expiredREFUND_CREATED- Refund createdREFUND_UPDATED- Refund updatedREFUND_COMPLETED- Refund completedREFUND_REJECTED- Refund rejectedREFUND_FAILED- Refund failed
5.2.3 Webhook Payload Examples
Multi-Card Completion:
{
"id": "evt_abc123",
"eventType": "MULTI_CARD_COMPLETED",
"merchantId": "trust_my_group",
"createdAt": "2025-10-06T14:30:00Z",
"multiCardId": "mc_abc123",
"data": {
"id": "mc_abc123",
"status": "COMPLETED",
"amountMoney": {
"amount": 25000,
"currency": "USD"
},
"capturedMoney": {
"amount": 25000,
"currency": "USD"
},
"metaData": {
"orderId": "tmg_order_123",
"customerId": "tmg_customer_456",
"createdBy": "trust_my_group"
},
"createdAt": "2025-10-06T14:00:00Z",
"completedAt": "2025-10-06T14:30:00Z"
}
}
Group Payment Completion:
{
"id": "evt_def456",
"eventType": "GROUP_PAYMENT_COMPLETED",
"merchantId": "trust_my_group",
"createdAt": "2025-10-06T14:30:00Z",
"groupPaymentId": "gp_def456",
"data": {
"id": "gp_def456",
"status": "COMPLETED",
"ownerId": "tmg_customer_456",
"memberIds": ["tmg_customer_456", "member_789", "member_012"],
"amountMoney": {
"amount": 50000,
"currency": "USD"
},
"capturedMoney": {
"amount": 50000,
"currency": "USD"
},
"metaData": {
"orderId": "tmg_order_123",
"ownerId": "tmg_customer_456",
"createdBy": "trust_my_group"
},
"createdAt": "2025-10-06T14:00:00Z",
"completedAt": "2025-10-06T14:30:00Z"
}
}
5.3 SDK Specifications
5.3.1 Multi-Card Widget Configuration
window.handsin({
merchantId: 'trust_my_group',
environment: 'production'
}).renderMultiCard('container-id', {
live: true,
merchantId: 'trust_my_group',
multiCardId: 'mc_abc123',
style: {
maxWidth: '600px'
},
multiCardDefaultEnabled: true,
onEvent: function(event) {
// Handle SDK events
switch(event.type) {
case 'payment_completed':
window.location.href = '/booking-success';
break;
case 'payment_failed':
showErrorMessage(event.data.error);
break;
}
}
});
5.3.2 SDK Event Handling
Available Events:
payment_completed- Payment successfully completedpayment_failed- Payment failedpayment_cancelled- Payment cancelled by userwidget_loaded- Widget finished loadingwidget_error- Widget encountered an error
Event Data Structure:
{
type: 'payment_completed',
data: {
paymentId: 'mc_abc123',
amount: 25000,
currency: 'USD'
}
}
Appendices
Appendix A: Hands In API Authentication
// Environment configuration
const config = {
development: {
apiUrl: 'https://api-dev.handsin.com',
apiKey: process.env.HANDSIN_DEV_API_KEY
},
production: {
apiUrl: 'https://api.handsin.com',
apiKey: process.env.HANDSIN_PROD_API_KEY
}
};
Appendix B: Error Handling Examples
// API Error Handling
async function createHandsInPayment(orderData) {
try {
const response = await fetch(`${config.apiUrl}/multi-card`, {
method: 'POST',
headers: {
'x-api-key': `${config.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(orderData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Hands In API Error: ${error.message}`);
}
return await response.json();
} catch (error) {
console.error('Payment creation failed:', error);
// Fallback to standard payment flow
return redirectToStandardCheckout(orderData);
}
}