package com.talentcard.server.business.payment; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Map; import com.talentcard.server.business.order.OrderRefundRequest; import com.talentcard.server.business.payment.huifu.HuifuDelayAccountGateway; import com.talentcard.server.business.payment.huifu.HuifuMiniProgramPaymentCommand; import com.talentcard.server.business.payment.huifu.HuifuMiniProgramPaymentGateway; import com.talentcard.server.business.portal.AppOrderCreateRequest; import com.talentcard.server.business.portal.AppOrderPortalService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; /** * 延迟入账与资金确认。 * *
业务规则:先付钱后核销的订单,钱必须留在通道「待确认」,核销确认后才分配给商户;支付即核销的
* 到店消费订单不延迟,也不该被这个流程影响。
*/
@SpringBootTest
@ActiveProfiles("test")
class PaymentFundConfirmationTests {
private static final String HUIFU_ID = "6666000226048687";
@Autowired
private JdbcTemplate jdbc;
@Autowired
private AppOrderPortalService orderPortalService;
@Autowired
private HuifuPaymentService paymentService;
@Autowired
private PaymentFundConfirmationService confirmationService;
@Autowired
private PaymentSettlementService settlementService;
@MockitoBean
private HuifuMiniProgramPaymentGateway paymentGateway;
@MockitoBean
private HuifuDelayAccountGateway delayGateway;
private Long merchantId;
private Long storeId;
private Long talentId;
private Long productId;
private String suffix;
@BeforeEach
void prepare() {
suffix = String.valueOf(System.nanoTime());
jdbc.update("insert into merchant(merchant_name, status, deleted) values (?, 1, 0)", "延迟入账商户 " + suffix);
merchantId = jdbc.queryForObject("select max(id) from merchant", Long.class);
jdbc.update("insert into talent_user(openid, name, mobile, status, deleted) values (?, ?, ?, 1, 0)",
"delay-" + suffix, "延迟入账用户", "13900000009");
talentId = jdbc.queryForObject("select max(id) from talent_user", Long.class);
jdbc.update("insert into talent_card(talent_id, card_no, status) values (?, ?, 1)", talentId, "DC" + suffix);
jdbc.update("insert into merchant_store(merchant_id, store_name, status, deleted) values (?, ?, 1, 0)",
merchantId, "延迟入账门店 " + suffix);
storeId = jdbc.queryForObject("select max(id) from merchant_store", Long.class);
// 下单要求商户的收款能力已确认:进件通过 + 斗拱商户号 + 能力快照(自动结算已关、取现可用)。
jdbc.update("""
insert into merchant_onboarding_application(merchant_id, store_id, application_no, subject_type, subject_name,
contact_name, contact_mobile, idempotency_key, status, provider_huifu_id,
auto_settle_status, cash_switch_status, merchant_stat, capability_verified_at)
values (?, ?, ?, 'individual', 'Delay owner', 'Delay owner', '13900000009', ?, 'approved', ?, '0', '1', 'NORMAL', current_timestamp)
""", merchantId, storeId, "MO-DELAY-" + suffix, "delay-onboarding-" + suffix, HUIFU_ID);
Long onboardingId = jdbc.queryForObject("select max(id) from merchant_onboarding_application", Long.class);
jdbc.update("""
insert into merchant_bank_card(merchant_id, store_id, bank_name, branch_name, city_code, card_tail, card_masked,
card_hash, status, idempotency_key, provider_token_no, reviewed_at)
values (?, ?, '中国建设银行', '攀枝花东区支行', '510400', '8888', '**** **** **** 8888', ?, 'approved', ?, ?, current_timestamp)
""", merchantId, storeId, "hash-delay-" + suffix, "delay-card-" + suffix, "T" + suffix);
Long cardId = jdbc.queryForObject("select max(id) from merchant_bank_card", Long.class);
jdbc.update("update merchant_onboarding_application set initial_bank_card_id = ? where id = ?", cardId, onboardingId);
jdbc.update("""
insert into merchant_product(merchant_id, product_no, product_type, product_name, original_price,
sale_price, stock, audit_status, status, deleted)
values (?, ?, 'GROUP_BUY', '延迟入账商品', 100.00, 100.00, -1, 2, 1, 0)
""", merchantId, "DELAY-PRODUCT-" + suffix);
productId = jdbc.queryForObject("select max(id) from merchant_product", Long.class);
}
@Test
void aDelayedOrderHoldsTheMoneyUntilItIsConfirmed() {
String orderNo = createOrder("GROUP_BUY", 100L);
String paymentNo = insertPaidPayment(orderNo, 100L, "pending");
when(delayGateway.confirm(any())).thenReturn(succeeded("1.00"));
assertThat(confirmationService.confirmForOrder(orderId(orderNo))).isEqualTo(
PaymentFundConfirmationService.CONFIRMED);
Map