Thêm cổng thanh toán
Hướng dẫn developer thêm payment gateway mới vào SimpPay.
Kiến trúc handler system
SimpPay sử dụng handler pattern với dynamic loading:
- Enum registry -
CardAPI/BankAPI/CoinsAPI - Handler interface -
PaymentHandler - Config class - Extends base config
- Handler implementation - Implements
PaymentHandler
Thêm card gateway
-
Thêm enum entry
src/main/java/me/typical/simppay/handler/HandlerRegistry.java:public enum CardAPI {THESIEUTOC("TheSieuToc", TSTHandler.class),CARD2K("Card2K", Card2KHandler.class),GACHTHE1S("Gachthe1s", GT1SHandler.class),THESIEURE("TheSieuRe", TSRHandler.class),DOITHE1S("Doithe1s", DT1SHandler.class),// Thêm gateway mớiNEW_GATEWAY("New Gateway", NewGatewayHandler.class);private final String displayName;private final Class<? extends PaymentHandler> handlerClass;CardAPI(String displayName, Class<? extends PaymentHandler> handlerClass) {this.displayName = displayName;this.handlerClass = handlerClass;}} -
Tạo config class
src/main/java/me/typical/simppay/config/types/card/NewGatewayConfig.java:package me.typical.simppay.config.types.card;import de.exlll.configlib.Configuration;import lombok.Data;import me.typical.simppay.config.annotations.Folder;import java.util.List;@Data@Configuration@Folder("card")public class NewGatewayConfig {private boolean enabled = true;// API credentialsprivate String apiKey = "your_api_key_here";private String secretKey = "your_secret_key_here";// Timeout settingsprivate int timeout = 300;private int interval = 10;// Supported card typesprivate List<String> supportedCardTypes = List.of("VIETTEL","MOBIFONE","VINAPHONE");// Supported amountsprivate List<Integer> supportedAmounts = List.of(10000, 20000, 50000, 100000,200000, 500000, 1000000);} -
Register config
src/main/java/me/typical/simppay/config/ConfigManager.java:private final List<Class<?>> configClasses = List.of(MainConfig.class,MessageConfig.class,// ... other configs ...NewGatewayConfig.class // Add here); -
Tạo handler class
src/main/java/me/typical/simppay/handler/card/newgateway/NewGatewayHandler.java:package me.typical.simppay.handler.card.newgateway;import me.typical.simppay.config.ConfigManager;import me.typical.simppay.config.types.card.NewGatewayConfig;import me.typical.simppay.handler.HandlerRegistry.CardAPI;import me.typical.simppay.handler.PaymentHandler;import me.typical.simppay.model.PaymentStatus;import okhttp3.*;import org.bukkit.entity.Player;import java.io.IOException;public class NewGatewayHandler implements PaymentHandler {private final OkHttpClient client = new OkHttpClient();private NewGatewayConfig config;@Overridepublic void setup() {this.config = ConfigManager.getInstance().getConfig(NewGatewayConfig.class);}@Overridepublic PaymentStatus send(Player player, String cardType,String serial, String pin,int amount, String orderId) {if (!config.isEnabled()) {return PaymentStatus.FAILED;}try {// Build requestRequestBody body = new FormBody.Builder().add("api_key", config.getApiKey()).add("card_type", cardType).add("serial", serial).add("pin", pin).add("amount", String.valueOf(amount)).add("order_id", orderId).build();Request request = new Request.Builder().url("https://api.newgateway.com/card/charge").post(body).build();// Execute requesttry (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) {return PaymentStatus.FAILED;}// Parse responseString responseBody = response.body().string();// TODO: Parse JSON and return appropriate statusreturn PaymentStatus.PENDING;}} catch (IOException e) {e.printStackTrace();return PaymentStatus.FAILED;}}@Overridepublic PaymentStatus check(String orderId) {try {Request request = new Request.Builder().url("https://api.newgateway.com/card/check?order_id=" + orderId).addHeader("Authorization", "Bearer " + config.getApiKey()).get().build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) {return PaymentStatus.PENDING;}String responseBody = response.body().string();// TODO: Parse JSON and return statusreturn PaymentStatus.PENDING;}} catch (IOException e) {e.printStackTrace();return PaymentStatus.PENDING;}}@Overridepublic CardAPI getAPI() {return CardAPI.NEW_GATEWAY;}@Overridepublic Object getConfig() {return config;}} -
Build và test
Terminal window ./gradlew buildCopy JAR vào server và test:
/napthe
Thêm banking gateway
Tương tự card gateway nhưng:
- Thêm vào
BankAPIenum - Handler implement
send(Player, int, String)thay vìsend(Player, String, String, String, int, String) - Config có thể khác (account number, bank code, etc.)
File structure
Danh mụcsrc/main/java/me/typical/simppay/
Danh mụchandler/
- HandlerRegistry.java (Add enum entry)
Danh mụccard/
Danh mụcnewgateway/
- NewGatewayHandler.java
Danh mụcconfig/
- ConfigManager.java (Register config)
Danh mụctypes/
Danh mụccard/
- NewGatewayConfig.java
PaymentHandler interface
public interface PaymentHandler {
/** * Setup handler (load config, init HTTP client, etc.) */ void setup();
/** * Send card payment request */ PaymentStatus send(Player player, String cardType, String serial, String pin, int amount, String orderId);
/** * Check payment status */ PaymentStatus check(String orderId);
/** * Get API enum */ Object getAPI();
/** * Get config instance */ Object getConfig();}PaymentStatus enum
public enum PaymentStatus { PENDING, // Đang xử lý SUCCESS, // Thành công FAILED, // Thất bại TIMEOUT, // Hết thời gian WRONG_AMOUNT, // Sai mệnh giá USED, // Thẻ đã dùng INVALID // Thẻ không hợp lệ}Best practices
-
Error handling: Catch tất cả exceptions và return appropriate status
-
Logging: Dùng
MessageUtil.debug()để log API requests/responses -
Timeout: Respect timeout settings từ config
-
Thread safety: Handler có thể được gọi từ nhiều threads
-
Config reload: Handler được re-initialized khi reload config
Testing
// Test trong handlerMessageUtil.debug("NewGatewayHandler: Sending card " + cardType + " " + amount);MessageUtil.debug("NewGatewayHandler: API Response: " + responseBody);MessageUtil.debug("NewGatewayHandler: Status: " + status);