fix: align payment teaching payee with prototype

chore/issuance-comments-zh
陈沅 1 month ago
parent aea498726c
commit 4b315995f3

@ -86,8 +86,12 @@ public class PaymentTrainingActionService {
switch (key) { switch (key) {
case "01:refresh-payer": case "01:refresh-payer":
return completed(payments.payer(actor)); return completed(payments.payer(actor));
case "01:refresh-payee": case "01:refresh-payee": {
return completed(payments.payee(required(request.getPayeeWalletId(), "收款钱包标识"))); String payeeWalletId = request.getPayeeWalletId();
return completed(payeeWalletId == null || payeeWalletId.trim().isEmpty()
? payments.context(null, actor).getPayee()
: payments.payee(payeeWalletId.trim()));
}
case "02:generate-timestamp": case "02:generate-timestamp":
return completed(value("timestamp", TIMESTAMP.format(Instant.now()))); return completed(value("timestamp", TIMESTAMP.format(Instant.now())));
case "02:concatenate-payment": case "02:concatenate-payment":
@ -149,8 +153,9 @@ public class PaymentTrainingActionService {
private Map<String, Object> paymentSource(PaymentActionRequest request, private Map<String, Object> paymentSource(PaymentActionRequest request,
ExperimentAttemptView attempt, ExperimentAttemptView attempt,
PaymentActor actor) { PaymentActor actor) {
String payeeWalletId = required(request.getPayeeWalletId(), "收款钱包标识"); String payeeWalletId = request.getPayeeWalletId();
PaymentContext context = payments.context(payeeWalletId, actor); PaymentContext context = payments.context(payeeWalletId == null || payeeWalletId.trim().isEmpty()
? null : payeeWalletId.trim(), actor);
if (request.getPayerWalletId() != null if (request.getPayerWalletId() != null
&& !context.getPayer().getWalletId().equals(request.getPayerWalletId().trim())) { && !context.getPayer().getWalletId().equals(request.getPayerWalletId().trim())) {
throw validation("付款钱包不属于当前用户"); throw validation("付款钱包不属于当前用户");
@ -237,11 +242,6 @@ public class PaymentTrainingActionService {
} }
} }
private String required(String value, String name) {
if (value == null || value.trim().isEmpty()) throw validation(name + "不能为空");
return value.trim();
}
private String fingerprint(String key, PaymentActionRequest request, ExperimentSubject subject) { private String fingerprint(String key, PaymentActionRequest request, ExperimentSubject subject) {
Map<String, Object> values = new LinkedHashMap<String, Object>(); Map<String, Object> values = new LinkedHashMap<String, Object>();
values.put("action", key); values.put("action", key);

@ -59,6 +59,9 @@ public class JdbcPaymentResourceRepository implements PaymentResourceRepository
@Override @Override
public PaymentParticipant loadPayee(String payeeWalletId) { public PaymentParticipant loadPayee(String payeeWalletId) {
if (teachingPayeeWhitelist.isTeachingWallet(payeeWalletId)) {
return loadParticipant(teachingPayeeWhitelist.resolve(payeeWalletId));
}
return loadParticipant(walletProjection.referenceForWallet(payeeWalletId)); return loadParticipant(walletProjection.referenceForWallet(payeeWalletId));
} }

@ -22,9 +22,16 @@ public class TeachingPaymentPayeeWhitelist {
private static final String BANK_A = "BKCHCNBJ00001"; private static final String BANK_A = "BKCHCNBJ00001";
private static final String BANK_B = "BKCHCNBJ00002"; private static final String BANK_B = "BKCHCNBJ00002";
private static final String PAYEE_A_USER = "00000000-0000-0000-0000-000000000951"; private static final String PAYEE_A_USER = "00000000-0000-0000-0000-000000000951";
private static final String PAYEE_B_USER = "00000000-0000-0000-0000-000000000952"; private static final String PAYEE_B_USER = "00000000-0000-0000-0000-000000000953";
private static final String PAYEE_A_WALLET = "WALLET_TRAINING_PAYEE_A"; private static final String PAYEE_A_WALLET = "WALLET_TRAINING_PAYEE_A";
private static final String PAYEE_B_WALLET = "WALLET_TRAINING_PAYEE_B"; private static final String PAYEE_B_WALLET =
"WALLET_B_7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6";
private static final PayeeDefinition PAYEE_A = new PayeeDefinition(
PAYEE_A_USER, PAYEE_A_WALLET, "TYPE_II", "CERT_" + PAYEE_A_WALLET,
BANK_A, "教学商业银行A", "ORG_TRAINING_PAYEE_A");
private static final PayeeDefinition PAYEE_B = new PayeeDefinition(
PAYEE_B_USER, PAYEE_B_WALLET, "一类钱包", "00C3D4E5F6A7B890",
BANK_B, "建设银行(BKCHCNBJ00002)", "ORG_5F6G7B8E9G0W");
private final JdbcTemplate jdbc; private final JdbcTemplate jdbc;
private final InstitutionIdentityCryptography cryptography; private final InstitutionIdentityCryptography cryptography;
@ -39,9 +46,21 @@ public class TeachingPaymentPayeeWhitelist {
@Transactional @Transactional
public WalletPrerequisiteReference resolveForDifferentBank(String payerBankCode) { public WalletPrerequisiteReference resolveForDifferentBank(String payerBankCode) {
PayeeDefinition payee = BANK_B.equals(payerBankCode) return resolve(BANK_B.equals(payerBankCode) ? PAYEE_A : PAYEE_B);
? new PayeeDefinition(PAYEE_A_USER, PAYEE_A_WALLET, BANK_A, "教学商业银行A", "ORG_TRAINING_PAYEE_A") }
: new PayeeDefinition(PAYEE_B_USER, PAYEE_B_WALLET, BANK_B, "教学商业银行B", "ORG_TRAINING_PAYEE_B");
public boolean isTeachingWallet(String walletId) {
return definitionFor(walletId) != null;
}
@Transactional
public WalletPrerequisiteReference resolve(String walletId) {
PayeeDefinition payee = definitionFor(walletId);
if (payee == null) throw conflict("教学收款方钱包不存在");
return resolve(payee);
}
private WalletPrerequisiteReference resolve(PayeeDefinition payee) {
ensureWallet(payee); ensureWallet(payee);
ensureCertificate(payee); ensureCertificate(payee);
ensureBankAccount(payee); ensureBankAccount(payee);
@ -50,6 +69,12 @@ public class TeachingPaymentPayeeWhitelist {
payee.bankCode, payee.organizationId); payee.bankCode, payee.organizationId);
} }
private PayeeDefinition definitionFor(String walletId) {
if (PAYEE_A_WALLET.equals(walletId)) return PAYEE_A;
if (PAYEE_B_WALLET.equals(walletId)) return PAYEE_B;
return null;
}
private void ensureWallet(PayeeDefinition payee) { private void ensureWallet(PayeeDefinition payee) {
List<WalletRow> wallets = jdbc.query("SELECT user_id,wallet_type,status FROM digital_wallet WHERE wallet_id=?", List<WalletRow> wallets = jdbc.query("SELECT user_id,wallet_type,status FROM digital_wallet WHERE wallet_id=?",
(rs, row) -> new WalletRow(rs.getString("user_id"), rs.getString("wallet_type"), (rs, row) -> new WalletRow(rs.getString("user_id"), rs.getString("wallet_type"),
@ -58,15 +83,20 @@ public class TeachingPaymentPayeeWhitelist {
try { try {
jdbc.update("INSERT INTO digital_wallet (wallet_id,user_id,wallet_type,status,balance,frozen_amount," + jdbc.update("INSERT INTO digital_wallet (wallet_id,user_id,wallet_type,status,balance,frozen_amount," +
"central_bank_confirmation_signature,opened_at,created_at,updated_at) " + "central_bank_confirmation_signature,opened_at,created_at,updated_at) " +
"VALUES (?,?, 'TYPE_II','ACTIVE',0,0,'TEACHING_PAYEE_CONFIRMED',CURRENT_TIMESTAMP," + "VALUES (?,?, ?,'ACTIVE',0,0,'TEACHING_PAYEE_CONFIRMED',CURRENT_TIMESTAMP," +
"CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)", payee.walletId, payee.userId); "CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)", payee.walletId, payee.userId, payee.walletType);
} catch (DuplicateKeyException exception) { } catch (DuplicateKeyException exception) {
List<WalletRow> afterConflict = jdbc.query(
"SELECT user_id,wallet_type,status FROM digital_wallet WHERE wallet_id=?",
(rs, row) -> new WalletRow(rs.getString("user_id"), rs.getString("wallet_type"),
rs.getString("status")), payee.walletId);
if (afterConflict.isEmpty()) throw conflict("教学收款方的钱包标识或用户标识已被占用");
ensureWallet(payee); ensureWallet(payee);
} }
return; return;
} }
WalletRow wallet = wallets.get(0); WalletRow wallet = wallets.get(0);
if (!payee.userId.equals(wallet.userId) || !"TYPE_II".equals(wallet.walletType) if (!payee.userId.equals(wallet.userId) || !payee.walletType.equals(wallet.walletType)
|| !"ACTIVE".equals(wallet.status)) { || !"ACTIVE".equals(wallet.status)) {
throw conflict("教学收款方钱包状态或归属异常"); throw conflict("教学收款方钱包状态或归属异常");
} }
@ -81,7 +111,7 @@ public class TeachingPaymentPayeeWhitelist {
jdbc.update("INSERT INTO wallet_certificate (certificate_serial,wallet_id,public_key,encrypted_private_key," + jdbc.update("INSERT INTO wallet_certificate (certificate_serial,wallet_id,public_key,encrypted_private_key," +
"filing_status,central_bank_root_signature,status,issued_at) " + "filing_status,central_bank_root_signature,status,issued_at) " +
"VALUES (?,?,?,?, 'REGISTERED','TEACHING_PAYEE_ROOT','VALID',CURRENT_TIMESTAMP)", "VALUES (?,?,?,?, 'REGISTERED','TEACHING_PAYEE_ROOT','VALID',CURRENT_TIMESTAMP)",
"CERT_" + payee.walletId, payee.walletId, keyPair.getPublicKey(), payee.certificateSerial, payee.walletId, keyPair.getPublicKey(),
walletCipher.encrypt(keyPair.getPrivateKey())); walletCipher.encrypt(keyPair.getPrivateKey()));
} catch (DuplicateKeyException exception) { } catch (DuplicateKeyException exception) {
ensureCertificate(payee); ensureCertificate(payee);
@ -137,14 +167,19 @@ public class TeachingPaymentPayeeWhitelist {
private static final class PayeeDefinition { private static final class PayeeDefinition {
private final String userId; private final String userId;
private final String walletId; private final String walletId;
private final String walletType;
private final String certificateSerial;
private final String bankCode; private final String bankCode;
private final String bankName; private final String bankName;
private final String organizationId; private final String organizationId;
private PayeeDefinition(String userId, String walletId, String bankCode, String bankName, private PayeeDefinition(String userId, String walletId, String walletType, String certificateSerial,
String bankCode, String bankName,
String organizationId) { String organizationId) {
this.userId = userId; this.userId = userId;
this.walletId = walletId; this.walletId = walletId;
this.walletType = walletType;
this.certificateSerial = certificateSerial;
this.bankCode = bankCode; this.bankCode = bankCode;
this.bankName = bankName; this.bankName = bankName;
this.organizationId = organizationId; this.organizationId = organizationId;

@ -204,11 +204,38 @@ class PaymentControllerTest {
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200)) .andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data.payer.walletId").value(PAYER_WALLET)) .andExpect(jsonPath("$.data.payer.walletId").value(PAYER_WALLET))
.andExpect(jsonPath("$.data.payee.walletId").value("WALLET_TRAINING_PAYEE_B")) .andExpect(jsonPath("$.data.payee.walletId").value(
"WALLET_B_7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6"))
.andExpect(jsonPath("$.data.payee.bankCode").value("BKCHCNBJ00002")); .andExpect(jsonPath("$.data.payee.bankCode").value("BKCHCNBJ00002"));
assertThat(jdbc.queryForObject("SELECT status FROM digital_wallet WHERE wallet_id=?", String.class, assertThat(jdbc.queryForObject("SELECT status FROM digital_wallet WHERE wallet_id=?", String.class,
"WALLET_TRAINING_PAYEE_B")).isEqualTo("ACTIVE"); "WALLET_B_7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6")).isEqualTo("ACTIVE");
}
@Test
void actionChainUsesTeachingPayeeWithoutASecondWallet() throws Exception {
String created = mockMvc.perform(post("/api/v1/payment/attempts").with(payer()))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
String attemptId = JsonPath.read(created, "$.data.attemptId");
executeAction(attemptId, "01", "refresh-payer", payer());
mockMvc.perform(post("/api/v1/payment/attempts/{id}/steps/01/actions/refresh-payee", attemptId)
.with(payer()).contentType(MediaType.APPLICATION_JSON).content("{}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.output.walletId").value(
"WALLET_B_7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6"))
.andExpect(jsonPath("$.data.output.walletType").value("一类钱包"))
.andExpect(jsonPath("$.data.output.certificateSerial").value("00C3D4E5F6A7B890"))
.andExpect(jsonPath("$.data.output.bankName").value("建设银行(BKCHCNBJ00002)"))
.andExpect(jsonPath("$.data.output.organizationId").value("ORG_5F6G7B8E9G0W"));
executeAction(attemptId, "02", "generate-timestamp", payer());
mockMvc.perform(post("/api/v1/payment/attempts/{id}/steps/02/actions/concatenate-payment", attemptId)
.with(payer()).contentType(MediaType.APPLICATION_JSON)
.content("{\"payerWalletId\":\"" + PAYER_WALLET + "\",\"payeeWalletId\":\"WALLET_B_7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6\",\"amount\":50,\"note\":\"教学支付\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.output.payeeWalletId").value(
"WALLET_B_7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6"));
} }
@Test @Test

Loading…
Cancel
Save