feat: improve member payment and order flows

This commit is contained in:
richjohnrambo
2026-10-15 15:03:39 +08:00
parent 06eaf748ed
commit acf50b9b7c
11 changed files with 172 additions and 22 deletions
+22 -1
View File
@@ -70,6 +70,7 @@ type PaymentAccountInput struct {
HolderName string `json:"holder_name"`
IdentityNo string `json:"identity_no"`
AccountNo string `json:"account_no"`
DigitalRMBCode string `json:"digital_rmb_code"`
WechatQRAttachmentID int64 `json:"wechat_qr_attachment_id"`
AlipayQRAttachmentID int64 `json:"alipay_qr_attachment_id"`
}
@@ -82,6 +83,7 @@ type PaymentAccount struct {
HolderMask string `json:"holder_mask"`
IdentityNoMask string `json:"identity_no_mask"`
AccountNoMask string `json:"account_no_mask"`
DigitalRMBCodeMask string `json:"digital_rmb_code_mask"`
WechatQRAttachmentID int64 `json:"wechat_qr_attachment_id"`
AlipayQRAttachmentID int64 `json:"alipay_qr_attachment_id"`
VerifyStatus string `json:"verify_status"`
@@ -299,12 +301,18 @@ func (s *Service) CreatePaymentAccount(ctx context.Context, tenantID, memberID i
if err != nil {
return nil, err
}
digitalRMBCodeCipher, err := s.encrypt(input.DigitalRMBCode)
if err != nil {
return nil, err
}
row, err := s.repository.CreatePaymentAccount(ctx, store.PaymentAccountCreation{
TenantID: tenantID, MemberID: memberID, AccountType: 1, BankCode: "BANK", BankName: input.BankName,
HolderCipher: holderCipher, HolderMask: maskName(input.HolderName),
IdentityNoCipher: identityNoCipher, IdentityNoHash: s.lookupHash(input.IdentityNo),
IdentityNoMask: maskIdentityNo(input.IdentityNo), AccountNoCipher: accountCipher,
AccountNoHash: s.lookupHash(input.AccountNo), AccountNoMask: maskAccount(input.AccountNo),
DigitalRMBCodeCipher: digitalRMBCodeCipher, DigitalRMBCodeHash: s.lookupHash(input.DigitalRMBCode),
DigitalRMBCodeMask: maskAccount(input.DigitalRMBCode),
WechatQRAttachmentID: input.WechatQRAttachmentID,
AlipayQRAttachmentID: input.AlipayQRAttachmentID,
})
@@ -360,10 +368,18 @@ func (s *Service) CreateWithdrawal(ctx context.Context, tenantID, memberID int64
return nil, store.ErrPaymentAccountInvalid
}
}
digitalRMBCode := ""
if account.DigitalRMBCodeCipher != "" {
digitalRMBCode, err = s.decrypt(account.DigitalRMBCodeCipher)
if err != nil {
return nil, store.ErrPaymentAccountInvalid
}
}
snapshot, err := json.Marshal(map[string]any{
"account_type": accountTypeName(account.AccountType), "bank_code": account.BankCode,
"bank_name": account.BankName, "holder_name": holderName,
"identity_no": identityNo, "account_no": accountNo,
"digital_rmb_code": digitalRMBCode,
"wechat_qr_attachment_id": account.WechatQRAttachmentID,
"alipay_qr_attachment_id": account.AlipayQRAttachmentID,
})
@@ -382,6 +398,8 @@ func (s *Service) CreateWithdrawal(ctx context.Context, tenantID, memberID int64
ExpectedBankName: account.BankName, ExpectedHolderCipher: account.HolderCipher,
ExpectedIdentityNoCipher: account.IdentityNoCipher, ExpectedIdentityNoHash: account.IdentityNoHash,
ExpectedAccountNoCipher: account.AccountNoCipher, ExpectedAccountNoHash: account.AccountNoHash,
ExpectedDigitalRMBCodeCipher: account.DigitalRMBCodeCipher,
ExpectedDigitalRMBCodeHash: account.DigitalRMBCodeHash,
ExpectedWechatQRAttachmentID: account.WechatQRAttachmentID,
ExpectedAlipayQRAttachmentID: account.AlipayQRAttachmentID,
Channel: *channel,
@@ -737,9 +755,11 @@ func normalizePaymentAccount(input *PaymentAccountInput) error {
input.HolderName = strings.TrimSpace(input.HolderName)
input.IdentityNo = strings.ToUpper(strings.NewReplacer(" ", "", "-", "").Replace(input.IdentityNo))
input.AccountNo = strings.NewReplacer(" ", "", "-", "").Replace(strings.TrimSpace(input.AccountNo))
input.DigitalRMBCode = strings.NewReplacer(" ", "", "-", "").Replace(strings.TrimSpace(input.DigitalRMBCode))
if !validHumanName(input.HolderName) || !identityNoPattern.MatchString(input.IdentityNo) ||
utf8.RuneCountInString(input.BankName) < 2 || utf8.RuneCountInString(input.BankName) > 100 ||
len(input.AccountNo) < 8 || len(input.AccountNo) > 32 || !allDigits(input.AccountNo) ||
len(input.DigitalRMBCode) < 6 || len(input.DigitalRMBCode) > 32 || !allDigits(input.DigitalRMBCode) ||
input.WechatQRAttachmentID <= 0 || input.AlipayQRAttachmentID <= 0 ||
input.WechatQRAttachmentID == input.AlipayQRAttachmentID {
return ErrAccountFieldsInvalid
@@ -750,7 +770,8 @@ func normalizePaymentAccount(input *PaymentAccountInput) error {
func paymentAccountFromStore(row store.PaymentAccount) PaymentAccount {
return PaymentAccount{ID: row.ID, AccountType: accountTypeName(row.AccountType), BankCode: row.BankCode,
BankName: row.BankName, HolderMask: row.HolderMask, IdentityNoMask: row.IdentityNoMask,
AccountNoMask: row.AccountNoMask, WechatQRAttachmentID: row.WechatQRAttachmentID,
AccountNoMask: row.AccountNoMask, DigitalRMBCodeMask: row.DigitalRMBCodeMask,
WechatQRAttachmentID: row.WechatQRAttachmentID,
AlipayQRAttachmentID: row.AlipayQRAttachmentID,
VerifyStatus: verifyStatusName(row.VerifyStatus), IsDefault: row.IsDefault == 1,
CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt}
+2
View File
@@ -103,11 +103,13 @@ func TestQuoteFailsClosed(t *testing.T) {
func TestNormalizePaymentAccount(t *testing.T) {
input := PaymentAccountInput{BankName: "中国工商银行", HolderName: "张三",
IdentityNo: "11010119900101123X", AccountNo: "6222 0000-0000 1234",
DigitalRMBCode: "1234 5678",
WechatQRAttachmentID: 8, AlipayQRAttachmentID: 9}
if err := normalizePaymentAccount(&input); err != nil {
t.Fatal(err)
}
if input.IdentityNo != "11010119900101123X" || input.AccountNo != "6222000000001234" ||
input.DigitalRMBCode != "12345678" ||
maskAccount(input.AccountNo) != "6222********1234" || maskIdentityNo(input.IdentityNo) != "1101**********123X" {
t.Fatalf("unexpected normalized input: %#v", input)
}