WeChat Official Account QR Code Payment Integration

Implementing QR code payment functionality within a WeChat Official Account requires proper configuration of merchant credantials including merchant ID, API key, and calllback URLs in the payment platform dashboard.

The frontend component displays the generated QR code using HTML:

<div id="qrcode-container"></div>
<div class="qr-image-wrapper">
    <img src="" id="qr-image"/>
</div>

JavaScript handles the payment initiasion process by sending the amount to the backend service:

function initiatePayment(amount) {
    $.ajax({
        method: 'POST',
        url: '/api/payment/generate-qr',
        data: { amount: amount },
        success: function(response) {
            renderQRCode(response.qrData);
        }
    });
}

The QR code rendering function converts the returned data into an image:

function renderQRCode(qrContent) {
    $('#qrcode-container').empty();
    $('#qrcode-container').qrcode(qrContent).hide();
    const canvasElement = $('#qrcode-container').find('canvas')[0];
    $('#qr-image').attr("src", canvasElement.toDataURL('image/png'));
}

The server-side controller processes the payment request and generates the QR code data:

@PostMapping("/generate-qr")
@ResponseBody
public Map<String, String> createPaymentQR(HttpServletRequest request, 
                                          @RequestParam int amount,
                                          @RequestParam String transactionId) throws Exception {
    
    String clientIP = NetworkUtils.getClientIP(request);
    
    PaymentRecord record = new PaymentRecord();
    record.setTransactionId(transactionId);
    record.setPaymentId(IdGenerator.generate());
    paymentRepository.save(record);

    String qrURL = WeChatPaymentService.generatePaymentQR(clientIP, amount, transactionId);
    
    Map<String, String> response = new HashMap<>();
    response.put("qrData", qrURL);
    return response;
}

The core payment utility handles communication with WeChat's payment API:

public class WeChatPaymentService {
    
    public static String generatePaymentQR(String clientIP, int amount, String orderId) throws Exception {
        
        String appId = PaymentConfig.WECHAT_APP_ID;
        String merchantId = PaymentConfig.MERCHANT_ID;
        String apiKey = PaymentConfig.API_SECRET;
        
        String timestamp = TimeUtils.getCurrentTimestamp();
        String randomStr = RandomStringUtils.random(4);
        String nonceString = timestamp.substring(8) + randomStr;
        
        String productName = "Digital Content Purchase";
        String orderNumber = orderId;
        String clientIPAddress = clientIP;
        String callbackURL = PaymentConfig.NOTIFICATION_ENDPOINT;
        String paymentMethod = "NATIVE";
        
        SortedMap<String, String> parameters = new TreeMap<>();
        parameters.put("appid", appId);
        parameters.put("mch_id", merchantId);
        parameters.put("nonce_str", nonceString);
        parameters.put("body", productName);
        parameters.put("out_trade_no", orderNumber);
        parameters.put("total_fee", String.valueOf(amount));
        parameters.put("spbill_create_ip", clientIPAddress);
        parameters.put("notify_url", callbackURL);
        parameters.put("trade_type", paymentMethod);
        
        String signature = SignatureGenerator.createSignature(parameters, apiKey);
        parameters.put("sign", signature);

        String xmlRequest = XMLBuilder.buildRequestXML(parameters);
        String apiResponse = HTTPClient.post(PaymentConfig.UNIFIED_ORDER_URL, xmlRequest);
        
        Map<String, String> parsedResponse = XMLParser.parseResponse(apiResponse);
        return parsedResponse.get("code_url");
    }
}

Utility class for signature generation and validation:

public class SignatureGenerator {
    
    public static boolean validateSignature(String encoding, 
                                         SortedMap<String, String> params, 
                                         String secretKey) {
        StringBuilder paramBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (!"sign".equals(key) && value != null && !value.isEmpty()) {
                paramBuilder.append(key).append("=").append(value).append("&");
            }
        }
        paramBuilder.append("key=").append(secretKey);
        
        String calculatedSignature = MD5Hasher.encode(paramBuilder.toString(), encoding).toLowerCase();
        String receivedSignature = params.get("sign").toLowerCase();
        return receivedSignature.equals(calculatedSignature);
    }
    
    public static String createSignature(String encoding, 
                                       SortedMap<String, String> params, 
                                       String secretKey) {
        StringBuilder signatureBase = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (value != null && !value.isEmpty() && !"sign".equals(key) && !"key".equals(key)) {
                signatureBase.append(key).append("=").append(value).append("&");
            }
        }
        signatureBase.append("key=").append(secretKey);
        return MD5Hasher.encode(signatureBase.toString(), encoding).toUpperCase();
    }
}

Configuration constants for payment integration:

public class PaymentConfig {
    public static final String WECHAT_APP_ID = "your_app_id_here";
    public static final String MERCHANT_ID = "your_merchant_id_here";
    public static final String API_SECRET = "your_api_secret_here";
    public static final String NOTIFICATION_ENDPOINT = "https://yoursite.com/api/payment/callback";
    public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    public static final String APP_SECRET = "your_app_secret_here";
}

XML processing utilities:

public class XMLBuilder {
    public static String buildRequestXML(SortedMap<String, String> parameters) {
        StringBuilder xmlBuilder = new StringBuilder();
        xmlBuilder.append("<xml>");
        for (Map.Entry<String, String> entry : parameters.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
                xmlBuilder.append("<").append(key).append("><![CDATA[").append(value).append("]]></").append(key).append(">");
            } else {
                xmlBuilder.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
            }
        }
        xmlBuilder.append("</xml>");
        return xmlBuilder.toString();
    }
}

Tags: WeChat Pay QR Code Payment java Spring Boot Payment Gateway

Posted on Tue, 26 May 2026 17:52:24 +0000 by nafetski