Hang Seng
About This Page
What: Hang Seng's deposit statement matching, eDDA/eDDI direct debit, corporate online banking withdrawal business rules, and key differences from HSBC Audience: PMs needing in-depth understanding of Hang Seng integration details Prerequisites: Bank Capability Matrix, HSBC (recommended to read HSBC first; Hang Seng focuses on differences) Reading time: 4 minutes Owner: Deposit Product Manager
Key takeaway: Hang Seng and HSBC share the FPS system but differ significantly in implementation — Hang Seng uses HASE Protocol (non-standard JSON), while HSBC uses standard FPS XML. Recommended to read the HSBC page first, then Hang Seng differences.
Capability Overview
| Capability | Supported | Protocol/Channel |
|---|---|---|
| Deposit Statement Matching | ✅ | Multiple statement types (ATM/GT/WY/ZP/BP) |
| eDDA Authorization | ✅ | eDDA API (internal SRPC, bank-side HTTPS REST) |
| eDDI Direct Debit | ✅ | eDDI API (internal SRPC, bank-side HTTPS REST) |
| Withdrawal | ✅ | Corporate online banking (method=hase) |
| FPS | ❌ | — |
| BST | ❌ | — |
Hang Seng is the first bank integrated with eDDI on moomoo — project design documents explicitly state "first brokerage to integrate Hang Seng eDDI service."
Key Differences from HSBC
Both Hang Seng and HSBC support eDDA/eDDI, but implementation details differ significantly:
| Dimension | HSBC | Hang Seng |
|---|---|---|
| eDDA Model | Mandate authorization | Simplified model (direct payer/payee) |
| Authorization requires OTP | Yes (SMS verification code) | No (digital certificate instead) |
| Authentication | Token + Basic Auth | P12 digital certificate + SHA256-RSA signature |
| Concurrency Control | Token bucket (optimistic lock, 4/sec) | Serialization (pessimistic lock, one at a time) |
| Bank Return Status | ACSC/RJCT/ACCP/ACSP | S(Success)/F(Failed)/P(Processing) |
| Trading Session Awareness | None | Designed but currently disabled |
| Grayscale Deployment | — | Supported (dual API + dual credentials) |
| Certificate Source | — | Hongkong Post e-Cert (USB delivery) |
Deposit: Statement Types & Matching Rules
Hang Seng deposit statements are divided into 5 types, each with different matching strategies:
| Statement Type | Meaning | Matching Conditions | Auto-deposit |
|---|---|---|---|
| WY | Online banking transfer | Amount similar + Name exact, date window -3~+2 days | ✅ Only auto-deposit type |
| ATM | ATM deposit | Amount exact, by batch time | ❌ |
| GT | Counter deposit | Amount exact, by batch time | ❌ |
| ZP | Cheque deposit | Amount exact + Name optional, date window -3~+2 days | ❌ |
| BP | Bill payment | Amount exact + Bill account number | ❌ |
| Default | Other | Amount similar, date window -3~+2 days | ❌ |
WY Type Auto-Deposit Conditions
Only WY (online banking transfer) type can auto-deposit, requiring all 4 conditions simultaneously:
- Deposit notification type is normal deposit (
NOTICE_TYPE_NORMAL) - Currency exact match
- Amount exactly equal
- English name exact match
If any condition is not met, WY statements fall back to "similar matching," requiring manual review.
ATM/GT Batch Time Matching
For ATM and GT types, date matching doesn't look at the statement date, but rather the batch import time (atm_date) — records imported in the same second are treated as the same batch. This is because ATM/counter deposit bank statements may arrive at significantly different times from the actual deposit time.
Amount Tolerance
| Currency | Tolerance | Applicable Types |
|---|---|---|
| HKD | CRM - 20 ≤ Statement ≤ CRM | WY/Default (during similar matching) |
| USD | CRM - 3 ≤ Statement ≤ CRM | WY/Default (during similar matching) |
ATM, GT, ZP, and BP types require exact amount match, tolerance does not apply.
eDDA/eDDI Direct Debit
eDDA Authorization: No OTP Required, Digital Certificate Instead
Unlike HSBC, Hang Seng's eDDA doesn't require the user to input an OTP SMS verification code. Instead, it authenticates request legitimacy through digital certificate signing. This means:
- User authorization flow is shorter (no need to wait for and enter SMS code)
- But security depends on certificate management — certificate expiration or loss affects the entire eDDI service
Digital Certificate Details
- Source: Hongkong Post e-Cert (ecert.gov.hk)
- Format: P12 (PKCS#12), USB delivery
- Signing Algorithm: SHA256 + RSA-PKCS1_v1_5
- Signing Steps:
- SHA256 hash the request content
- Convert hash to uppercase hex string
- SHA256 hash the hex string again
- Sign with RSA private key
- Base64 encode result and place in
msgSignaturerequest header
eDDI Request Parameters
Hang Seng eDDI is more simplified than HSBC, not requiring mandate_identification:
| Parameter | Description | Example |
|---|---|---|
| payer_bank | Payer bank code | "024" (Hang Seng) |
| payer_id | Payer account number | "123456789001" |
| payer_name | Payer name | User's name |
| payee_bank | Payee bank code | "024" (Hang Seng) |
| payee_account_number | Payee account | Current account (3-6-3) or savings account (3-1-6) |
| transaction_amount | Amount (11 digits, including 2 decimal places) | "00000012345" = 123.45 |
| currency | Currency | "HKD" |
| payment_purpose_code | Purpose code | 01=Fee, 07=E-commerce, 08=Other |
eDDI State Machine
Feature Disabled
The new_blank trading session awareness feature has been commented out in code (handles.py L37-42). Currently all requests enter wait_process state regardless of session. The following description is the original design, not current runtime behavior.
Trading Session Awareness (original design): If an eDDI instruction is created during non-Hong Kong stock trading session (e.g., nighttime), it won't be sent to the bank immediately, but enters new_blank state waiting for the trading session to begin. This is a Hang Seng-unique feature; HSBC has no such restriction.
Bank Return Status Codes
| Status Code | Meaning | System Action |
|---|---|---|
| S | Debit successful | → end_ok, create deposit flow |
| F | Debit failed | → end_reject, record failure reason |
| P | Processing | → pending, continue querying |
Channel Interface Overview
| Dimension | Description |
|---|---|
| Protocol Type | HTTPS REST + P12 digital certificate signing |
| Authentication | Hongkong Post e-Cert (P12 PKCS#12 + SHA256-RSA signing) |
| Bank Code | "024" |
| Concurrency Model | Serialization (pessimistic lock, one request at a time) |
| Trading Session Awareness | Designed but currently disabled (code commented out) |
eDDI Request Fields Complete Table
EDDIProcedure (Hang Seng eDDI format) complete fields:
| Field | Type | Description | Values/Format |
|---|---|---|---|
payer_bank | string | Payer bank code | "024"=Hang Seng, or other bank codes |
payer_id | string | Payer ID | AccountNumber (e.g. "123456789001") or CustomerID (e.g. "012N18020900000202") |
payer_name | string | Payer name | e.g. "Logistic Company A" |
debtor_reference | string | Payer reference | e.g. "Shipping Fee" |
transaction_amount | string(11) | Amount (11 digits zero-padded, last 2 are cents) | "00000012345" = 123.45 HKD |
payee_bank | string | Payee bank code | "024" |
payee_account_number | string | Payee account | Current account: 3-6-3 format "333666666333", Savings: 3-1-6 format "3331666666" |
payer_type | string | Payer type | "01"=AccountNumber, "02"=CustomerID |
currency | string | Currency | "HKD" |
payment_purpose_code | string | Payment purpose code | "01"=Charge, "02"=Fee, "03"=Donation, "04"=Rent, "05"=Insurance, "06"=Utility, "07"=E-commerce, "08"=Other |
client_transaction_id | string | Client transaction ID | System-generated, e.g. "ABC000123456789" |
Response Fields:
| Field | Description |
|---|---|
transaction_id | Hang Seng transaction ID |
acknowledgement_id | Hang Seng acknowledgement ID |
error_code | "000"=Success, others require developer documentation |
error_reason | Error description |
reject_code | Rejection code |
Amount Format
Hang Seng eDDI uses an 11-digit zero-padded string, with the last 2 digits being cents:
| Actual Amount | Transmission Format | Description |
|---|---|---|
| 123.45 HKD | "00000012345" | 123.45 x 100 = 12345, zero-pad to 11 digits |
| 10,000.00 HKD | "00001000000" | 10000.00 x 100 = 1000000, zero-pad to 11 digits |
| 0.50 HKD | "00000000050" | 0.50 x 100 = 50, zero-pad to 11 digits |
Conversion rule: Multiply decimal amount by 100, convert to integer, then left-pad with zeros to 11 characters.
Account Number Format
Hang Seng bank account numbers have two formats depending on account type:
| Account Type | Format | Structure | Example |
|---|---|---|---|
| Current Account | 3-6-3 | BBB-AAAAAA-CCC | "333666666333" |
| Savings Account | 3-1-6 | BBB-T-AAAAAA | "3331666666" |
The system automatically determines account type based on the account number length.
Bank Error Codes (Complete Table)
| Error Code | Meaning | Type | Handling |
|---|---|---|---|
000 | Success | — | Normal processing |
BRC_8I1 | Insufficient balance | Business | Notify user to top up |
BRC_8RW | Authorization issue | Business | Re-authorize |
BRC_8RZ | Account abnormal | Business | Contact bank |
FP2414 | Electronic authorization not found | Authorization | Re-sign |
FP2415 | eDDA not activated | Authorization | Wait for activation |
FP2416 | eDDA expired | Authorization | Re-sign |
FP2417 | Exceeded eDDA limit | Authorization | Increase limit |
CAC_018 | Bank service unavailable | System | Auto retry |
CAC_021 | Bank service unavailable | System | Auto retry |
CAC_022 | Duplicate transaction | Idempotency | Track as pending |
CAC_998 | Bank timeout | System | Auto retry |
CAC_999 | Bank timeout | System | Auto retry |
BRC_8RK | Bank service unavailable | System | Auto retry |
BRC_8RM | Bank service unavailable | System | Auto retry |
Other CAC_* | Other rejections | Business | Immediately reject |
Error Code Classification
- BRC prefix: Bank Business Return Code, divided into retryable (
8RK/8RM) and non-retryable (8I1/8RW/8RZ) - FP prefix: eDDA authorization-related errors, usually requiring user re-signing or waiting for activation
- CAC prefix: Channel Access Code (communication errors), most are auto-retryable;
CAC_022is a duplicate transaction requiring special handling
moomoo Matching Rules (HangSengMatch) — By Statement Type
The system routes to different matching branches based on Hang Seng statement type codes, each with different matching strictness:
| Statement Type | Code | Matching Rules (Amount / Name / Date) | Auto-deposit |
|---|---|---|---|
| Online Banking (WY) | WY | Amount exact+tolerance, name exact match, standard window (-3~+2 days) | ✅ Only auto-deposit type |
| ATM | ATM | Amount exact, batch time (atm_date) | ❌ |
| Counter (GT) | GT | Amount exact, batch time (atm_date) | ❌ |
| Cheque (ZP) | ZP | Amount exact, date window (-3~+2 days) | ❌ |
| Bill (BP) | BP | Amount exact, bill account verification | ❌ |
WY Auto-Deposit 4 Conditions (all must be met for auto-deposit):
notice_type=NOTICE_TYPE_NORMAL(normal deposit notification)- Currency exact match
- Amount exactly equal (stricter than assisted matching, doesn't use tolerance logic)
- English name exact match
ATM/GT "Batch Time Matching" explanation: Doesn't look at the statement date field, but rather atm_date — records imported in the same second are treated as the same batch. This is because ATM/counter deposit bank statements may arrive at significantly different times from the actual deposit time, making import batch time more reliable.
Serialization Control
Hang Seng requires one eDDI request at a time — no concurrency. The system implements serialization using InnoDB row locks:
| Parameter | Description |
|---|---|
| Lock Type | InnoDB row-level pessimistic lock (UserLock table) |
| Lock Granularity | One request at a time |
| Lock Identifier | selnum=886 (default) |
| Grayscale Lock | selnum_gray / selnum_formal locked separately |
Why does Hang Seng need serialization while HSBC doesn't? Hang Seng's eDDI interface doesn't support concurrent requests — if two debits arrive simultaneously, the bank returns an error. So the system must queue them one by one. HSBC supports a degree of concurrency (rate controlled via token bucket).
Grayscale Deployment
Hang Seng eDDI supports grayscale release, allowing some users to use the new version interface:
| Dimension | Grayscale Environment | Production Environment |
|---|---|---|
| API Endpoint | ddi_url_gray | ddi_url |
| Credentials | gray_usrname / gray_psw | username / password |
| Certificate | gray_profile_id | profile_id |
| Serial Lock | selnum_gray | selnum_formal |
| Routing Rules | Grayscale user list + user_id modulo | Default |
Withdrawal: Corporate Online Banking
Hang Seng withdrawal uses corporate online banking batch remittance, with method code hase.
| Dimension | Description |
|---|---|
| Method Code | TRANSFER_METHOD_HASE = 'hase' |
| Classification | Electronic bank method (allEBankMethod) |
| Name | Hang Seng Online Banking Withdrawal |
| Automation Level | Semi-automated (requires corporate online banking operation) |
Same as HSBC, Hang Seng withdrawal goes through the corporate online banking channel, not eDDI (eDDA/eDDI is only used for deposit direct debit).
Change Guide
| Change Requirement | Modification Location | Description |
|---|---|---|
| Modify WY auto-deposit conditions | HangSengMatch.php → WY branch | Adjust name/amount/currency matching conditions |
| Add new statement type | HangSengMatch.php → switch branch | Add matching rules for new type |
| Modify amount tolerance | MatchRule.php → amountSimilar() | Adjust HKD -20 / USD -3 thresholds |
| eDDI add purpose code | sba_hase_eddi request parameters | Add payment_purpose_code |
| Modify serialization strategy | sba_hase_eddi → table_model.py | Adjust UserLock logic |
| Replace digital certificate | sba_hase_eddi/conf/ | Replace P12 file and password |
| Modify grayscale ratio | sba_hase_eddi → gray_control.py | Adjust user list/modulo rules |
| Add retryable error codes | sba_hase_eddi → handles.py | Modify hase_bank_service_not_available_map |
| Modify withdrawal approval | Task.php → $stepTemplates | Adjust Hang Seng withdrawal approval template |
| Certificate renewal | Hongkong Post e-Cert website | Apply for new certificate, replace P12 file |
Common Customer Complaints Top 3
| # | User Feedback | Cause | CS Script |
|---|---|---|---|
| 1 | "eDDA debit failed" | BRC_8I1 insufficient balance | "Please ensure your Hang Seng bank account has sufficient balance and try again" |
| 2 | "eDDA suddenly stopped working" | Consecutive insufficient balance → bank auto-cancels authorization | "Your eDDA authorization may have been cancelled by the bank, please re-sign the eDDA authorization" |
| 3 | "Amount debited differs from application" | Bank service fee deduction | "Cross-bank transfers may incur service fees, actual debit amount is subject to bank charges" |
Monitoring & Alerts
| Alert Item | Trigger Condition | Severity | Handling Steps |
|---|---|---|---|
| eDDI concurrent lock conflict | InnoDB row lock wait timeout | Medium | Check sba_hase_eddi concurrency config, reduce simultaneous debits |
| BRC_8I1 consecutive alerts | Same user multiple insufficient balance | Medium | Remind user to top up in advance, consecutive occurrences may cause auto-cancellation of authorization |
| Web notification reception failure | Hang Seng web notification callback failure | High | Check callback endpoint availability, inspect HTTP status codes |
| Bank service unavailable | CAC_018/021/998/999 | Medium | System auto-delays retry; contact Hang Seng tech support if persistent |
After Reading
| I want to... | Go to |
|---|---|
| Compare HSBC's eDDA/eDDI differences | HSBC |
| Understand the full matching engine logic | Matching & Auto-Deposit |
| Check eDDA/eDDI error codes | Unified Error Code Center |
| See Hang Seng's position among all banks | Bank Capability Matrix |
| Understand eDDA/eDDI's role in the architecture | System Architecture & Data Flow |