Production-Grade Payment System Guide: Java Implementation for Alipay & WeChat All-Channel Integration

This article explains why a payment system is more than a simple SDK integration, presents a four‑layer architecture (access, state, event, governance), details state machines, idempotent callbacks, outbox pattern, reconciliation, high‑concurrency handling, observability, security, and provides Java code examples for integrating Alipay and WeChat across all channels.

Cloud Architecture
Cloud Architecture
Cloud Architecture
Production-Grade Payment System Guide: Java Implementation for Alipay & WeChat All-Channel Integration

Production‑Grade Payment System Overview

Payment is often mistaken for a simple SDK call. In production it is a strongly stateful, strongly consistent, and strongly auditable money‑collaboration system.

Why Payment Is Not an Ordinary Integration

Which source (order DB, Redis, front‑end page, or third‑party channel) determines the final payment status?

How to handle duplicate or out‑of‑order callbacks?

How to recover when only part of the post‑payment actions (delivery, points, membership, notifications) succeed?

How to support multiple channels (Alipay, WeChat, App, Mini‑Program, PC) with a unified entry?

How to guarantee fast channel confirmation during traffic spikes?

How to close the loop when end‑of‑day reconciliation finds discrepancies?

Goals

Explain the state and trust model behind Alipay and WeChat integration.

Provide a production‑grade, unified payment middle‑platform architecture.

Cover the full chain: order creation, callbacks, queries, refunds, reconciliation, compensation, and governance.

Offer Java/Spring Boot skeleton code.

Discuss high concurrency, observability, and security.

Four‑Layer Architecture

Access Layer : unified entry, authentication, request‑level idempotency, parameter standardization, channel routing.

State Layer : payment and refund state machines, state transition rules, optimistic locking.

Event Layer : reliable event chain for callbacks, delivery, points, coupons, notifications.

Governance Layer : metrics, tracing, audit, reconciliation, compensation, risk control, rate‑limiting, gray‑release, rollback.

Access Layer Details

Authentication & merchant identification.

Request‑level idempotency (e.g., pay:create:{merchantId}:{bizOrderId}).

Parameter standardization.

Channel & product routing.

State Layer Details

Payment order state machine:

INIT → PAYING → SUCCESS / FAILED / CLOSED → REFUNDING → REFUNDED

.

Refund order state machine: INIT, ACCEPTED, PROCESSING, SUCCESS, FAILED, CLOSED.

Key principle: once SUCCESS is reached it cannot be overwritten by ordinary failures; duplicate events must not cause duplicate business actions.

# State transition example (simplified)
# CurrentState   Event                     NextState   Remark
INIT            CreateChannelOrderSuccess PAYING      Sync order creation
INIT            CreateChannelOrderFail    FAILED      Parameter or channel error
PAYING          ChannelAsyncSuccess      SUCCESS     Main success path
PAYING          ChannelQuerySuccess      SUCCESS     Compensation path
PAYING          ChannelCloseOrTimeout    CLOSED      Timeout or merchant close
SUCCESS         RefundRequest            REFUNDING   Start refund flow
REFUNDING      RefundSuccess            REFUNDED    Refund completed

Event Layer Details

Callbacks are first turned into internal trusted events.

Event ingestion is idempotent (key = {channel}:{eventId}).

After a successful state transition an outbox record is created for asynchronous downstream processing.

Outbox table stores

aggregate_type, aggregate_id, event_type, payload_json, status, retry_count, next_retry_time

.

Governance Layer Details

Compensation jobs query long‑running PAYING orders and reconcile with the channel.

End‑of‑day reconciliation compares channel statements, local payment orders, and downstream consumption results.

Core metrics: order success rate, channel call success, callback verification failures, PAYING backlog, reconciliation diff rate, outbox backlog, MQ latency.

Unified logging fields:

traceId, merchantId, bizOrderId, paymentId, channel, eventType, statusBefore, statusAfter

.

Audit logs record who, when, which order, what action, before/after status, reason, and remarks.

Security: certificate management, callback signature verification, nonce & timestamp checks, WAF protection, secret management via KMS/Vault.

Key Code Samples

Create Payment Command (Java record)

public record CreatePaymentCommand(
        Long merchantId,
        String bizOrderId,
        PayChannel channel,
        PayProduct product,
        long amountCent,
        String subject,
        String description,
        String clientIp,
        String openId,
        String appId,
        Instant expireTime,
        String requestId,
        Map<String, String> metadata
) {}

Payment Channel Result (Java record)

public record PaymentChannelResult(
        String paymentId,
        String channelTradeNo,
        String payUrl,
        String codeUrl,
        String prepayId,
        Instant expireTime
) {}

PaymentChannelGateway Interface

public interface PaymentChannelGateway {
    PayChannel channel();
    PaymentChannelResult createOrder(ChannelCreateOrderRequest request);
    ChannelQueryResult queryOrder(ChannelQueryRequest request);
    ChannelRefundResult createRefund(ChannelRefundRequest request);
    ChannelRefundQueryResult queryRefund(ChannelRefundQueryRequest request);
    ChannelCloseResult closeOrder(ChannelCloseRequest request);
}

PaymentApplicationService (core orchestration)

@Service
public class PaymentApplicationService {
    private final PaymentOrderRepository paymentOrderRepository;
    private final PaymentChannelRegistry paymentChannelRegistry;
    private final IdempotencyService idempotencyService;

    @Transactional
    public CreatePaymentResponse createPayment(CreatePaymentCommand command) {
        idempotencyService.checkAndLock(
                "pay:create:" + command.merchantId() + ":" + command.bizOrderId(),
                Duration.ofSeconds(10)
        );
        PaymentOrder existed = paymentOrderRepository.findByMerchantAndBizOrder(
                command.merchantId(), command.bizOrderId());
        if (existed != null) {
            return CreatePaymentResponse.fromExisting(existed);
        }
        PaymentOrder paymentOrder = PaymentOrder.create(command);
        paymentOrderRepository.save(paymentOrder);
        PaymentChannelGateway gateway = paymentChannelRegistry.mustGet(command.channel());
        PaymentChannelResult result = gateway.createOrder(ChannelCreateOrderRequest.from(paymentOrder, command));
        paymentOrder.markPaying(result.channelTradeNo());
        paymentOrderRepository.updateAfterChannelAccept(paymentOrder);
        return CreatePaymentResponse.from(paymentOrder, result);
    }
}

Alipay Adapter Skeleton

@Component
public class AlipayChannelGateway implements PaymentChannelGateway {
    private final AlipayClient alipayClient;
    private final AlipayProperties properties;

    @Override
    public PayChannel channel() { return PayChannel.ALIPAY; }

    @Override
    public PaymentChannelResult createOrder(ChannelCreateOrderRequest request) {
        try {
            AlipayTradePrecreateRequest apiRequest = new AlipayTradePrecreateRequest();
            apiRequest.setNotifyUrl(properties.getNotifyUrl());
            apiRequest.setBizContent("""
                {"out_trade_no":"%s","total_amount":"%s","subject":"%s","timeout_express":"30m"}
                """.formatted(
                request.paymentId(),
                FenAmount.of(request.amountCent()).toYuanString(),
                request.subject()
            ));
            AlipayTradePrecreateResponse response = alipayClient.certificateExecute(apiRequest);
            if (!response.isSuccess()) {
                throw new ChannelBizException("ALIPAY_PRECREATE_FAIL", response.getSubMsg());
            }
            return new PaymentChannelResult(
                    request.paymentId(),
                    response.getTradeNo(),
                    null,
                    response.getQrCode(),
                    null,
                    request.expireTime()
            );
        } catch (AlipayApiException ex) {
            throw new ChannelInvokeException("ALIPAY_API_ERROR", ex.getErrMsg(), ex);
        }
    }
    // other methods omitted for brevity
}

WeChat Adapter Skeleton

@Component
public class WechatPayChannelGateway implements PaymentChannelGateway {
    private final NativePayService nativePayService;
    private final JsapiServiceExtension jsapiServiceExtension;

    @Override
    public PayChannel channel() { return PayChannel.WECHAT; }

    @Override
    public PaymentChannelResult createOrder(ChannelCreateOrderRequest request) {
        if (request.product() == PayProduct.NATIVE) {
            PrepayRequest prepayRequest = new PrepayRequest();
            prepayRequest.setOutTradeNo(request.paymentId());
            prepayRequest.setDescription(request.subject());
            prepayRequest.setNotifyUrl(request.notifyUrl());
            Amount amount = new Amount();
            amount.setTotal((int) request.amountCent());
            prepayRequest.setAmount(amount);
            NativePrepayResponse response = nativePayService.prepay(prepayRequest);
            return new PaymentChannelResult(
                    request.paymentId(),
                    null,
                    null,
                    response.getCodeUrl(),
                    null,
                    request.expireTime()
            );
        }
        if (request.product() == PayProduct.JSAPI) {
            PrepayRequest requestBody = new PrepayRequest();
            requestBody.setOutTradeNo(request.paymentId());
            requestBody.setDescription(request.subject());
            requestBody.setNotifyUrl(request.notifyUrl());
            Amount amount = new Amount();
            amount.setTotal((int) request.amountCent());
            requestBody.setAmount(amount);
            Payer payer = new Payer();
            payer.setOpenid(request.openId());
            requestBody.setPayer(payer);
            PrepayWithRequestPaymentResponse response = jsapiServiceExtension.prepayWithRequestPayment(requestBody);
            return new PaymentChannelResult(
                    request.paymentId(),
                    null,
                    null,
                    null,
                    response.getPrepayId(),
                    request.expireTime()
            );
        }
        throw new IllegalArgumentException("Unsupported pay product: " + request.product());
    }
    // other methods omitted for brevity
}

Trusted Callback Controllers

WeChat Callback

@RestController
@RequestMapping("/callbacks/wechat")
public class WechatCallbackController {
    private final WechatCallbackVerifier verifier;
    private final PaymentEventIngressService paymentEventIngressService;

    @PostMapping("/pay")
    public ResponseEntity<Map<String, String>> callback(
            @RequestHeader("Wechatpay-Timestamp") String timestamp,
            @RequestHeader("Wechatpay-Nonce") String nonce,
            @RequestHeader("Wechatpay-Signature") String signature,
            @RequestHeader("Wechatpay-Serial") String serial,
            @RequestBody String body) {
        VerifiedWechatCallback callback = verifier.verifyAndDecrypt(timestamp, nonce, signature, serial, body);
        paymentEventIngressService.ingressChannelEvent(ChannelCallbackCommand.wechat(callback));
        return ResponseEntity.ok(Map.of("code", "SUCCESS", "message", "成功"));
    }
}

Alipay Callback

@RestController
@RequestMapping("/callbacks/alipay")
public class AlipayCallbackController {
    private final AlipayNotifyVerifier notifyVerifier;
    private final PaymentEventIngressService paymentEventIngressService;

    @PostMapping("/pay")
    public String callback(HttpServletRequest request) {
        Map<String, String> params = RequestParamUtils.toFlatMap(request);
        notifyVerifier.verify(params);
        paymentEventIngressService.ingressChannelEvent(ChannelCallbackCommand.alipay(params));
        return "success";
    }
}

Event Ingress Service (Idempotent Ingestion)

@Service
public class PaymentEventIngressService {
    private final PaymentEventRepository paymentEventRepository;
    private final PaymentOrderRepository paymentOrderRepository;
    private final OutboxRepository outboxRepository;

    @Transactional
    public void ingressChannelEvent(ChannelCallbackCommand command) {
        String idempotencyKey = command.channel() + ":" + command.eventId();
        if (paymentEventRepository.existsByIdempotencyKey(idempotencyKey)) {
            return;
        }
        PaymentEvent event = PaymentEvent.fromCallback(command, idempotencyKey);
        paymentEventRepository.save(event);
        PaymentOrder paymentOrder = paymentOrderRepository.mustFindByPaymentId(command.paymentId());
        boolean changed = paymentOrder.applyChannelEvent(event);
        if (changed) {
            paymentOrderRepository.update(paymentOrder);
            outboxRepository.save(OutboxMessage.paymentSucceeded(paymentOrder));
        }
    }
}

Outbox Publisher Job

@Service
public class OutboxPublisherJob {
    private final OutboxRepository outboxRepository;
    private final MessagePublisher messagePublisher;

    @Transactional
    public int publishBatch() {
        List<OutboxMessage> messages = outboxRepository.lockBatchForPublish(100);
        int count = 0;
        for (OutboxMessage message : messages) {
            try {
                messagePublisher.publish(message.topic(), message.businessKey(), message.payload());
                outboxRepository.markSent(message.id());
                count++;
            } catch (Exception ex) {
                outboxRepository.markRetry(message.id(), Backoff.nextTime(message.retryCount()));
            }
        }
        return count;
    }
}

Compensation Job for PAYING Orders

@Service
public class PaymentReconcileJob {
    private final PaymentOrderRepository paymentOrderRepository;
    private final PaymentChannelRegistry paymentChannelRegistry;
    private final PaymentEventIngressService paymentEventIngressService;

    public void reconcilePayingOrders() {
        List<PaymentOrder> payingOrders = paymentOrderRepository.findNeedQueryOrders(
                PaymentStatus.PAYING, Instant.now().minusSeconds(120), 200);
        for (PaymentOrder order : payingOrders) {
            try {
                PaymentChannelGateway gateway = paymentChannelRegistry.mustGet(order.getPayChannel());
                ChannelQueryResult result = gateway.queryOrder(ChannelQueryRequest.of(order));
                if (result.isSuccess()) {
                    paymentEventIngressService.ingressChannelEvent(ChannelCallbackCommand.fromQueryResult(result));
                } else if (result.isClosed()) {
                    paymentEventIngressService.ingressChannelEvent(ChannelCallbackCommand.fromClosedResult(result));
                }
            } catch (Exception ex) {
                // log compensation failure, do not break the batch
            }
        }
    }
}

High‑Concurrency Governance

Identify bottlenecks such as DB connection pool saturation, Redis hot keys, synchronous channel calls, and MQ consumption delays. Isolate resources with dedicated thread pools and HTTP connection pools per channel. Apply layered rate‑limiting: order‑level, merchant‑level, channel‑level, and activity‑level limits.

Observability & Audit

Core metrics: order success rate, channel call success, callback verification failures, PAYING backlog, compensation success rate, reconciliation diff rate, outbox backlog, MQ latency.

Unified log fields:

traceId, merchantId, bizOrderId, paymentId, channel, eventType, statusBefore, statusAfter

.

Audit logs capture who, when, which order, what action, before/after status, reason, and remarks.

Security Governance

Certificates and private keys are stored in KMS/Secrets Manager; rotation is performed via gray‑release.

Callback signature verification, timestamp tolerance, nonce checks, and WAF protection are mandatory.

Sensitive personal data (real name, full card number, phone, ID) are masked in logs.

Production Checklist (selected items)

Merchant authentication completed.

Request‑level idempotency for order creation.

Channel routing rules support gray release.

State machines for payment and refund are immutable.

Callback entry verifies signature before persisting events.

Outbox and state update share the same transaction.

Compensation jobs isolated from online traffic.

Certificate expiration alerts configured.

Core metrics have alert thresholds.

Audit logs are traceable.

Conclusion

A production‑grade payment system must be treated as a money‑state engine rather than a simple SDK wrapper. By separating concerns into Access, State, Event, and Governance layers, handling idempotent callbacks, using an outbox pattern for reliable downstream processing, and implementing compensation and reconciliation, the system becomes robust, observable, and secure. The provided Java/Spring Boot skeletons for Alipay and WeChat illustrate how to materialize these principles in code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javareconciliationstate machineHigh ConcurrencySpring BootIdempotencypaymentWeChat PayAlipayoutbox
Cloud Architecture
Written by

Cloud Architecture

Focuses on cloud‑native and distributed architecture engineering, sharing practical solutions and lessons learned. Covers microservice governance, Kubernetes, observability, and stability engineering to help your systems run stable, fast, and cost‑effectively.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.