Skip to content

HSBC

About This Page

What: HSBC's MT910 statement collection, eDDA/eDDI direct debit, corporate online banking withdrawal complete business rules and channel-side interface fields Audience: PMs and integration developers needing in-depth understanding of HSBC integration details Prerequisites: Bank Capability MatrixReading time: 15 minutes Owner: Deposit Product Manager

Key takeaway: HSBC is the only bank simultaneously supporting three deposit methods (MT910 Push, eDDA/eDDI Pull, corporate online banking withdrawal), and has the most complex fee logic — tolerance must cover HKD 65 / USD 14 fee deductions.


Capability Overview

CapabilitySupportedProtocol/ChannelCore Service
Deposit Statement CollectionMT910 (SWIFT message)hsbc_bank_flow_service (Python)
eDDA AuthorizationeDDA API (SRPC)sba_hsbc_eddi proxy (Python)
eDDI Direct DebiteDDI API (SRPC)sba_hsbc_eddi worker (Python)
WithdrawalCorporate online banking batch remittancemethod=hsbc
FPS
BST

HSBC is the only bank simultaneously supporting "three deposit methods": MT910 statement collection (user-initiated transfer) + eDDA/eDDI direct debit (system auto-debit) + corporate online banking withdrawal.


Channel Interface Overview

HSBC and moomoo have three independent interface systems, each operating independently:

#Interface SystemProtocolDirectionPurposeService
1MT910 SWIFT MessageSFTP + GPG encryptionHSBC → moomooDeposit statement collection (bank pushes credit notification after user transfer)hsbc_bank_flow_service
2eDDA/eDDI APIREST API (SRPC wrapped)moomoo ↔ HSBCDirect debit deposit (eDDA signs authorization + eDDI executes debit)sba_hsbc_eddi
3Corporate Online BankingOnline banking batch filemoomoo → HSBCWithdrawal (batch remittance)method=hsbc

The three systems are mutually independent: MT910 handles user-initiated transfer statements, eDDA/eDDI handles system auto-debit, and corporate online banking handles withdrawals. The only intersection is the matching engine — when the deposit method is eDDI direct debit, MT910 statement matching actively skips that application.


Channel-Side Interface Details

MT910 Message Complete Field Parsing

MT910 (Confirmation of Credit) is a SWIFT standard credit confirmation message. HSBC pushes encrypted MT910 files via SFTP, which moomoo decrypts and parses line by line.

Complete Field Mapping

SWIFT TagField NameDescriptionExtraction Rule
:20:txn_ref_noTransaction reference number (unique within file)Full value → short_transaction_id
:21:related_refRelated reference numberOptional, full value
:25:account_idBank account identifierFull account → bank_account
:32A:date_ccy_amountDate + Currency + AmountSee parsing rules below → date + ccy + credit
:50K:ordering_customerRemitter name and accountLine 1 → account, Line 2+ → name (remove prefix) → customer_account + en_name
:52A: / :52D:ordering_institutionRemitting bankBIC code or multi-line address → remarks (concatenated)
:56A: / :56D:intermediaryIntermediary bank (cross-border remittance)Optional, BIC or multi-line
:72:sender_to_receiverTransaction details/remarksFree text, ≤512 characters → remarks (concatenated)

:32A: Parsing Rules

:32A: is the core field for deposits, containing date, currency, and amount as a continuous string:

Format: YYMMDDCCCNNNN.NN
Example: 250827HKD50000.00
PartPositionRuleExample
DateFirst 6 digits"20" + YYMMDD → standard date2508272025-08-27
CurrencyDigits 7-9ISO 4217 three-character codeHKD / USD / CNY
AmountFrom digit 10 to enddecimal, with decimal point50000.00

Supported currencies: HKD, USD, CNY.

:50K: Name Cleaning Rules

The :50K: field usually contains multi-line information; parsing requires name cleaning:

LineContentProcessing
Line 1Usually an account numberExtract as customer_account
Line 2 and afterRemitter nameConcatenate as en_name

Name cleaning — remove the following prefixes before matching:

PrefixDescription
MRMister
MRSMadam
MISSMiss
MSMs

Example: :50K: content is 123456789\nMR CHAN TAI MAN, parsed result: customer_account=123456789, en_name=CHAN TAI MAN.

SFTP Configuration

ParameterDescription
ProtocolSFTP (SSH File Transfer Protocol)
SSH KeyRSA key pair
Public Key Locationsrc/conf/HSBC_SSP_PREPROD_SFTP_KEY.pub
Private Key Locationsrc/conf/id_rsa
GPG EncryptionRequired (all downloaded files are GPG-decrypted)
Supported CurrenciesHKD, USD, CNY
Configuration Filessftp_config.ini + parse_flow.ini

Deduplication Mechanism

Deduplication by TransactionReferenceNumber (:20: tag value) — this value is unique within a file. When writing to raw_hsbc_bank_flow table, if the reference number already exists, it's skipped to avoid duplicate deposits.


eDDA API Complete Fields

eDDA (Electronic Direct Debit Authorization) is the authorization interface for users to allow moomoo to automatically debit from their HSBC account. Below are complete field definitions for each request/response.

BankAccountInfo (Nested Structure)

Bank account information structure used in multiple places across eDDA/eDDI interfaces:

FieldTypeRequiredDescription
bank_codestring(3)Bank code. Values "004" = HSBC, "024" = Hang Seng
account_identificationstring(12-35)Bank account number. HSBC personal account
currencystring(3)Currency. Value "HKD"
account_scheme_namestring(4)Account type. Value "BBAN" (default)

EDDAApplyReq (Apply Authorization Request)

FieldTypeRequiredDescription
req_idstring(1-32)Request ID. Globally unique
merchant_request_identificationstring(1-30)Merchant registration ID. Generated by moomoo
creditor_referencestring(1-35)DDA identifier. Links to eDDA authorization
ultimate_debtor_namestring(1-140)Ultimate payer name. User's real name
debtor_namestring(1-140)Payer name. Same as above or agent name
debtor_accountBankAccountInfoPayer bank account. User's HSBC account
creditor_namestring(1-140)Payee name. moomoo company name
creditor_accountBankAccountInfoPayee bank account. moomoo's HSBC receiving account
debtor_private_identificationstring(1-12)HKID or passport number. User's identity document number
debtor_private_identification_scheme_namestring(4)Document type. Values "HKID" or "NIDN"
debtor_mobile_numberstringMobile number. For receiving OTP
maximum_amount_currencystring(3)Limit currency. Value "HKD"
maximum_amountstring(≤18)Maximum debit amount. Value "99999999.00" = no limit
frequency_typestring(≤4)Debit frequency type. Values "MNTH" = Monthly / "ADHO" = On demand
duration_from_datestringAuthorization start date. NULL = effective immediately
duration_to_datestring(10)Authorization end date. NULL = permanently valid
otp_hold_indicatorboolWhether OTP verification required. HSBC same-bank = true
sms_language_codestring(≤10)OTP SMS language. Values "eng" / "zh-s" / "zh-t"

otp_hold_indicator Explanation

When otp_hold_indicator=true, HSBC pauses the flow at the Apply stage, waiting for the user to enter an OTP verification code before continuing. This is HSBC's security requirement for same-bank transfers.

EDDAApplyRsp (Apply Authorization Response)

FieldTypeDescription
resp_codestringBank response code (success/failure)
mandate_identificationstringDDA reference ID (used in subsequent OTP confirmation and debits)
otp_identification_numberstringOTP identifier (used for OTPConfirm request)
otp_mobile_numberstringMasked mobile number (e.g. ****1234, for frontend display)
mandate_statusstringAuthorization status (usually pending confirmation at Apply stage)
reject_reason_listRejectReasonListRejection reason list (returned when Apply fails)

OTPConfirmReq (OTP Confirmation Request)

FieldTypeRequiredDescription
req_idstring(1-32)Request ID
mandate_identificationstringDDA reference (from EDDAApplyRsp)
creditor_accountBankAccountInfoPayee account (moomoo account)
otp_identification_numberstringOTP ID (from EDDAApplyRsp)
otp_passwordstringSMS verification code entered by user

After OTP confirmation succeeds, the eDDA authorization enters bank review stage, with EDDAEnquiry polling for final status.


eDDI API Complete Fields

eDDI (Electronic Direct Debit Instruction) is the actual debit request sent from moomoo to HSBC after eDDA authorization is complete.

EDDILaunchReq (Initiate Debit Request)

FieldTypeRequiredDescription
req_idstring(1-32)Request ID. Globally unique
merchant_instruction_identificationstring(≤35)DDI request ID. Debit instruction ID generated by moomoo
mandate_identificationstring(≤35)DDA reference. Either this or creditor_reference
debtor_namestring(≤140)Payer name. User's name
debtor_accountBankAccountInfoPayer account. User's HSBC account
creditor_accountBankAccountInfoPayee account. moomoo's HSBC receiving account
creditor_referencestring(≤35)Payee reference. Either this or mandate_identification
instructed_amount_currencystring(3)Debit currency. Value "HKD"
instructed_amountstring(≤18)Debit amount. e.g. "10000.00"
remittance_informationstring(≤140)Remittance information. Recommended to leave empty
sms_language_codestring(≤10)SMS language. Debit notification language

mandate_identification vs creditor_reference

These two fields are either/or to locate an eDDA authorization. mandate_identification is the bank-returned DDA reference ID, creditor_reference is moomoo's DDA identifier. In practice, mandate_identification is used preferentially.

EDDIRsp (Debit Response)

FieldTypeDescription
payment_statusstringTransaction status: ACSC/RJCT/ACCP/ACSP
resp_codestringBank response code
reject_reason_listRejectReasonListRejection reason list
transaction_idstringBank transaction ID
statusstring"return_ok" = bank has responded / "empty" = bank has not yet responded

When status="empty", the bank hasn't finished processing yet; continue polling via the EDDIEnquiry interface.

EDDIEnquiry (Query Debit Result)

FieldTypeRequiredDescription
req_idstring(1-32)Request ID
merchant_instruction_identificationstring(≤35)DDI request ID (same as in Launch)
creditor_accountBankAccountInfoPayee account

The payment_status field in the query response determines the final debit outcome; see the "eDDI State Machine" section below for specific status code meanings.


eDDI Error Codes

When an eDDI debit is rejected by the bank (payment_status=RJCT), reject_reason_list returns specific error codes:

Error CodeMeaningHandling Suggestion
MPP02020eDDA cancelled or doesn't existGuide user to re-sign eDDA authorization
MPP02021Payer account closedNotify user bank account is invalid
MPP02022Exceeded eDDA limitGuide user to increase eDDA limit in HSBC App
MPP02038eDDA authorization dormantNeed to contact HSBC to reactivate
MPP02039eDDA expiredGuide user to re-sign eDDA authorization
MPP05000Exceeded account withdrawal limitSuggest user reduce deposit amount or contact bank
MFD10007Duplicate transactionAlready processed on system side, no retry needed

More Error Codes

For the complete eDDA/eDDI error code list, see Unified Error Code Center.


Deposit: MT910 Statement Collection

What is MT910

MT910 is a SWIFT standard bank message format used by banks to notify enterprises "a payment has arrived." HSBC pushes transaction information to moomoo via MT910 messages.

From July 2024, HSBC upgraded to real-time push (previously daily batch files), significantly improving deposit response speed.

Data Flow

File Processing Flow

StepOperationDescription
1SFTP pullPull files from HSBC SFTP server by date
2Download locallyStore in mt910_files/YYYYMM/ directory
3GPG decryptDecrypt with private key, generate plain_ prefixed plaintext file
4Parse messagesRegex match SWIFT tags, extract fields
5Write to databaseStore in raw_hsbc_bank_flow, deduplicate by transaction reference
6Convert to flowCall resolve_bank_flows() to write to flow system

Processing frequency: Executed daily, processing yesterday's and today's files.

File Naming Convention

MT910.<Futu account>.<HSBC merchant code>.<timestamp>.TXT Example: MT910.741071039201.PC000001581.20210301120000.TXT

Monitoring Metrics
Metric IDMeaning
1002976Script startup
936360Exception error
936363-365ListFiles operation stats
936366-368GetFiles operation stats
604339Duplicate transaction
604340Database write failure

eDDA/eDDI Direct Debit

What are eDDA and eDDI

ConceptFull NamePurpose
eDDAElectronic Direct Debit AuthorizationUser authorizes moomoo to auto-debit from HSBC account
eDDIElectronic Direct Debit Instructionmoomoo sends debit instruction to HSBC, debiting from user's account

Simply put: eDDA is "signing authorization," eDDI is "executing debit." The user first signs eDDA authorization, then each time a deposit is made, the system automatically sends an eDDI debit.

eDDA Authorization Flow

eDDA Authorization Three Steps:

StepRPC MethodUser ActionBank Action
1. Apply AuthorizationEDDAApplyFill in bank account infoSend OTP SMS to user's phone
2. OTP ConfirmationOTPConfirmEnter received SMS codeVerify OTP
3. Query StatusEDDAEnquiryWaitReturn authorization status

eDDI Debit Execution

eDDI is the debit automatically executed by the system when users deposit. There are three deposit types:

TypeEnum ValueDescription
FUND_HOLD11Fund regular investment, simultaneously freezes deposit
STOCK_HOLD21Stock regular investment, simultaneously freezes deposit
FUND_PURCHASE_HOLD31Fund purchase, simultaneously freezes deposit

There are also three non-freezing base types: FOUNDING_AIP(1)=Fund regular investment, STOCK_MP(2)=Stock regular investment, FUND_PURCHASE(3)=Fund purchase.

eDDI State Machine

eDDI Procedure Complete Status Definition:

StatusMeaningSourceSubsequent Transitions
newDebit instruction createdSystem initializationwait_process
wait_processInitialization complete, ready to sendSystem processingpending / end_ok / end_reject
pendingSent to bank, awaiting confirmationLaunch returns status="empty" or ACCP/ACSPend_ok / end_reject / repairing
end_okDebit successfulBank returns ACSCTerminal state, create deposit flow
end_rejectBank rejectedBank returns RJCTTerminal state, record rejection reason
repairingException needs manual interventionSystem determines exceptionpending (retry after manual repair)

Bank-Returned Debit Status Codes:

Status CodeFull NameMeaningSystem Action
ACSCAccepted Settlement CompletedDebit successfulend_ok, create deposit flow
RJCTRejectedBank rejectedend_reject, record rejection reason
ACCPAccepted Clearing in ProgressClearing in progresspending, continue polling
ACSPAccepted Settlement in ProgressSettlement in progresspending, continue polling
emptyBank has not yet respondedContinue polling

Polling & Retry Mechanism

After eDDI execution, results aren't immediate; bank polling is needed:

ParameterValueDescription
Normal polling interval1-4 seconds randomAvoid fixed frequency impacting bank
Peak period polling interval1-19 seconds randomExtended interval during peak
Peak period07:00-07:10Configurable
Retry threshold 13 timesTriggers alert when exceeded
Retry threshold 210 timesEscalates alert when exceeded
HTTP 429 handlingAuto retryQueue and retry when bank rate-limits

Rate Limiting (Token Bucket)

HSBC has call frequency limits on eDDA/eDDI interfaces. The system uses a token bucket mechanism to control request rate:

ParameterValue
Production rate4 requests/second
Default rate1 request/second
Lock strategyOptimistic lock (timestamp field version check)
Normal polling interval1-4 seconds random
Peak polling interval1-19 seconds random (07:00-07:10)
Retry threshold 13 times → trigger alert
Retry threshold 210 times → escalate alert

Why optimistic lock? Early implementation used pessimistic lock (exclusive lock), which caused lock timeouts in production (default 50 seconds), leading to request thread pile-up. After switching to optimistic lock with timestamp field version checks, lock waiting issues were avoided.

Token Bucket Working Principle
  1. Daemon process adds tokens to token_bucket table at configured rate (4/sec)
  2. Each request attempts to consume one token: UPDATE ... SET token=token-1 WHERE token>0 AND timestamp=@version
  3. If timestamp doesn't match (optimistic lock conflict), retry
  4. If token=0 (tokens depleted), request is rate-limited
eDDI Monitoring Metrics
Metric IDMeaning
561904eDDI initialization
561910Debit successful
561911Debit failed
561912Debit pending confirmation
561913Timeout retry
561925Retry exceeded 3 times
561926Retry exceeded 10 times
573198Duplicate transaction (MFD10007)
589305HTTP error

moomoo-Side Adaptation

MT910 → BankFlow Complete Mapping

After MT910 message parsing, conversion to moomoo's unified BankFlow structure via resolve_bank_flows():

MT910 FieldSWIFT TagBankFlow FieldConversion Rule
txn_ref_no:20:short_transaction_idDirect mapping, used as dedup key
account_id:25:bank_accountDirect mapping
date (from :32A:):32A: first 6 digitsdate"20" + YYMMDDYYYY-MM-DD
ccy (from :32A:):32A: digits 7-9ccyISO 4217 three-char → internal currency code
amount (from :32A:):32A: from digit 10creditdecimal string → amount
ordering_customer line 1:50K:customer_accountRemitter's bank account
ordering_customer line 2+:50K:en_nameCleaned English name
ordering_institution:52A: / :52D:remarks (concatenated)BIC code or bank name
sender_to_receiver_info:72:remarks (concatenated)Remarks appended to remarks
bank_typeFixed value: HSBC
flow_directionFixed value: CREDIT

Matching Rules (HsbcMatch)

HSBC matching has two tolerance layers — strict tolerance for auto-deposit, relaxed tolerance for manual review:

ScenarioHKD ToleranceUSD ToleranceTrigger Condition
Auto-depositCRM - 65 ≤ Statement ≤ CRMCRM - 14 ≤ Statement ≤ CRMName exact match + bank account match
Manual reviewCRM - 420 ≤ Statement ≤ CRMCRM - 60 ≤ Statement ≤ CRMName similar + amount close

Why is tolerance in the negative direction? Bank transfers may have service fees deducted (e.g., wire transfer fees, intermediary bank fees), causing actual credited amount to be less than user-initiated amount. Tolerance only opens downward — statement amount can be less than CRM amount (fee deduction), but not more.

Bank Account Matching Special Processing:

ProcessingDescription
Remove prefixHSBC account numbers sometimes have a 3-digit bank code prefix (e.g., 004), system auto-removes prefix before comparison
Code listBesides 004 (HSBC), also handles 024 (Hang Seng) and other affiliated bank codes
Fuzzy matchAfter prefix removal, align lengths then compare character by character

eDDA Deposit Exclusion: If the deposit application method is DEPOSIT_METHOD_HSBC_EDDA (eDDI direct debit), the statement matching engine is skipped — because eDDI has its own independent crediting process and neither needs nor should match with MT910 statements.

eDDI → Deposit Flow Mapping

After eDDI debit succeeds (ACSC), the system automatically creates a deposit flow with different field sources than MT910:

BankFlow FieldData SourceDescription
short_transaction_idEDDIRsp → transaction_idBank-returned transaction ID
bank_accountEDDILaunchReq → debtor_accountUser's HSBC account
dateSystem current dateDebit execution date
ccyEDDILaunchReq → instructed_amount_currencyDebit currency
creditEDDILaunchReq → instructed_amountDebit amount (no fee deduction)
en_nameEDDILaunchReq → debtor_namePayer name
customer_accountEDDILaunchReq → debtor_account.account_identificationUser's bank account
deposit_methodFixed value: DEPOSIT_METHOD_HSBC_EDDA

eDDA Authorization Status Mapping

eDDA authorization maintains an independent state machine on the moomoo side:

mandate_status (Bank)moomoo StatusDescription
ACTVActiveCan normally initiate eDDI debits
SUSPSuspendedTemporarily cannot debit, wait for restoration
CANCCancelledUser or bank cancelled authorization, re-sign needed
EXPRExpiredPast duration_to_date, re-sign needed

Withdrawal: Corporate Online Banking

HSBC withdrawal uses corporate online banking batch remittance, with method code hsbc.

DimensionDescription
Method CodeTRANSFER_METHOD_HSBC = 'hsbc'
ClassificationElectronic bank method (allEBankMethod)
NameHSBC Online Banking Withdrawal
Automation LevelSemi-automated (requires corporate online banking operation)
Supported CurrenciesHKD, USD
Approval TemplateTiered by amount, see Task.php$stepTemplates

Note: HSBC withdrawal uses the corporate online banking channel, not eDDI. eDDA/eDDI is only used for deposit direct debit; withdrawal is a completely independent channel.

Withdrawal Operation Flow

StepOperationRole
1System generates withdrawal batch fileAutomatic
2Operations imports into corporate online bankingOperations staff
3Corporate online banking approvalFinance approver
4Bank executes remittanceHSBC
5System confirms withdrawal resultAutomatic/Operations

Change Guide

Change RequirementModification LocationDescription
Modify auto-deposit toleranceHsbcMatch.phpamountSimilarForAuto()Adjust HKD -65 / USD -14 thresholds
Modify manual review toleranceHsbcMatch.phpamountSimilar()Adjust HKD -420 / USD -60 thresholds
Add matching dimensionHsbcMatch.phpmatch() methodAdd new matching conditions
Modify bank account prefix rulesHsbcMatch.php → bank code arrayAdd/modify 3-digit prefixes
Add new eDDI deposit typeEddiDepositType.phpAdd new enum value + SBA configuration
Modify token bucket ratesba_hsbc_eddi token_bucket daemon configurationAdjust frequency parameter
Modify polling intervalsba_hsbc_eddi_worker.iniAdjust rush/normal intervals
eDDA parameter changessba_hsbc_eddi proxy layerModify authorization parameters (frequency, amount, etc.)
MT910 field changeshsbc_bank_flow_serviceparse.pyModify SWIFT tag parsing rules
Modify SFTP configurationsftp_config.ini + parse_flow.iniModify server/account/path
GPG key replacementhsbc_bank_flow_service/conf/Update private key and password
Modify withdrawal approvalTask.php$stepTemplatesAdjust HSBC withdrawal approval template
Add new eDDI error code handlingsba_hsbc_eddi worker → error handling logicAdd new error code mapping
Modify :50K: name cleaning ruleshsbc_bank_flow_serviceparse.pyAdjust prefix cleaning list

Common Customer Complaints Top 3

#User FeedbackCauseCS Script
1"eDDA signing failed"Phone number/name doesn't match HSBC records"Please confirm your moomoo registered name and phone number match your HSBC bank registration info"
2"Verification code expired"OTP has a short validity period"Please request a new verification code and enter it promptly after receiving"
3"eDDA debit exceeded limit"Exceeded the maximum debit amount set during authorization"Your debit amount exceeds the authorization limit, please reduce the amount or contact HSBC to adjust the limit"

Monitoring & Alerts

Alert ItemTrigger ConditionSeverityHandling Steps
MT910 SFTP connection failureSFTP file transfer interruptedHighCheck SFTP configuration and network connectivity, confirm GPG key not expired
eDDI Token Bucket depleted4 tokens/sec rate limiting triggeredMediumCheck if large number of debit tasks are queued, reduce concurrency appropriately
eDDI batch debit timeoutDebit tasks not returning results for extended timeMediumCheck sba_hsbc_eddi logs, confirm bank-side is normal
GPG decryption failureMT910 file cannot be decryptedHighConfirm GPG key not expired or replaced, contact HSBC to update

After Reading

I want to...Go to
Compare Hang Seng's eDDA/eDDI differencesHang Seng
Understand the full matching engine logicMatching & Auto-Deposit
Check eDDA/eDDI error codesUnified Error Code Center
See HSBC's position among all banksBank Capability Matrix
Understand eDDA/eDDI's role in the architectureSystem Architecture & Data Flow
Understand how SBA orchestration layer schedules eDDISBA Fund Orchestration
View the general deposit statement collection processStatement Collection Channels
Was this page helpful?

内部业务文档 · 仅限 moomoo 团队使用