You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
471 lines
25 KiB
Java
471 lines
25 KiB
Java
package com.yau.digitalrmb.shared.wallet;
|
|
|
|
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
|
|
import com.yau.digitalrmb.shared.api.ErrorCode;
|
|
import com.yau.digitalrmb.shared.exception.BusinessException;
|
|
import org.springframework.dao.DuplicateKeyException;
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.sql.Timestamp;
|
|
import java.time.LocalDateTime;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.format.DateTimeParseException;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
@Service
|
|
public class WalletPrerequisiteProjectionService {
|
|
private static final DateTimeFormatter MODULE_TIMESTAMP = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
|
|
|
private final JdbcTemplate jdbc;
|
|
private final WalletPrivateKeyCipher privateKeyCipher;
|
|
|
|
public WalletPrerequisiteProjectionService(JdbcTemplate jdbc, WalletPrivateKeyCipher privateKeyCipher) {
|
|
this.jdbc = jdbc;
|
|
this.privateKeyCipher = privateKeyCipher;
|
|
}
|
|
|
|
@Transactional
|
|
public WalletPrerequisiteReference ensureForSubject(InstitutionKeySubject subject) {
|
|
SourceWallet source = loadCompletedSource(subject);
|
|
Institution institution = loadInstitution(subject);
|
|
String accountId = ensureBankAccount(source, institution);
|
|
ensureWallet(source);
|
|
ensureCertificate(source);
|
|
ensureContract(source);
|
|
ensureBinding(source.walletId, accountId);
|
|
return new WalletPrerequisiteReference(source.userId, source.schoolId, source.classId,
|
|
source.walletId, institution.bankCode, institution.organizationId);
|
|
}
|
|
|
|
@Transactional(readOnly = true)
|
|
public WalletPrerequisiteReference referenceForSubject(InstitutionKeySubject subject) {
|
|
SourceWallet source = loadCompletedSource(subject);
|
|
Institution institution = loadInstitution(subject);
|
|
return new WalletPrerequisiteReference(source.userId, source.schoolId, source.classId,
|
|
source.walletId, institution.bankCode, institution.organizationId);
|
|
}
|
|
|
|
@Transactional
|
|
public WalletPrerequisiteReference ensureForWallet(String walletId) {
|
|
if (blank(walletId)) throw prerequisite();
|
|
List<InstitutionKeySubject> subjects = jdbc.query(
|
|
"SELECT DISTINCT user_id,school_id,class_id FROM central_wallet_activation " +
|
|
"WHERE wallet_id=? AND wallet_activated=TRUE AND final_sent=TRUE " +
|
|
"AND cb_final_signature IS NOT NULL AND deleted=FALSE",
|
|
(rs, row) -> new InstitutionKeySubject(rs.getString("user_id"),
|
|
rs.getLong("school_id"), rs.getLong("class_id")), walletId.trim());
|
|
if (subjects.isEmpty()) throw prerequisite();
|
|
if (subjects.size() != 1) throw conflict("钱包标识对应多个实验主体");
|
|
return ensureForSubject(subjects.get(0));
|
|
}
|
|
|
|
@Transactional(readOnly = true)
|
|
public WalletPrerequisiteReference referenceForWallet(String walletId) {
|
|
if (blank(walletId)) throw prerequisite();
|
|
List<InstitutionKeySubject> subjects = jdbc.query(
|
|
"SELECT DISTINCT user_id,school_id,class_id FROM central_wallet_activation " +
|
|
"WHERE wallet_id=? AND wallet_activated=TRUE AND final_sent=TRUE " +
|
|
"AND cb_final_signature IS NOT NULL AND deleted=FALSE",
|
|
(rs, row) -> new InstitutionKeySubject(rs.getString("user_id"),
|
|
rs.getLong("school_id"), rs.getLong("class_id")), walletId.trim());
|
|
if (subjects.isEmpty()) throw prerequisite();
|
|
if (subjects.size() != 1) throw conflict("钱包标识对应多个实验主体");
|
|
return referenceForSubject(subjects.get(0));
|
|
}
|
|
|
|
private SourceWallet loadCompletedSource(InstitutionKeySubject subject) {
|
|
List<Activation> activations = jdbc.query(
|
|
"SELECT wallet_id,cb_final_signature,final_time FROM central_wallet_activation " +
|
|
"WHERE user_id=? AND school_id=? AND class_id=? AND wallet_activated=TRUE " +
|
|
"AND final_sent=TRUE AND cb_final_signature IS NOT NULL AND status='FINAL_SENT' " +
|
|
"AND deleted=FALSE ORDER BY created_at DESC LIMIT 1",
|
|
(rs, row) -> new Activation(rs.getString("wallet_id"),
|
|
rs.getString("cb_final_signature"), rs.getString("final_time")),
|
|
subject.getUserId(), subject.getSchoolId(), subject.getClassId());
|
|
if (activations.isEmpty()) throw prerequisite();
|
|
Activation activation = activations.get(0);
|
|
|
|
List<Application> applications = jdbc.query(
|
|
"SELECT COALESCE(account_bank,selected_bank) bank_name,bank_card_number,account_balance,wallet_type " +
|
|
"FROM wallet_application WHERE user_id=? AND school_id=? AND class_id=? " +
|
|
"AND status='SUBMITTED' AND deleted=FALSE AND bank_card_number IS NOT NULL " +
|
|
"AND account_balance IS NOT NULL AND COALESCE(account_bank,selected_bank) IS NOT NULL " +
|
|
"ORDER BY created_at DESC LIMIT 1",
|
|
(rs, row) -> new Application(rs.getString("bank_name"), rs.getString("bank_card_number"),
|
|
rs.getBigDecimal("account_balance"), rs.getString("wallet_type")),
|
|
subject.getUserId(), subject.getSchoolId(), subject.getClassId());
|
|
List<Certificate> certificates = jdbc.query(
|
|
"SELECT cert_private_key,cert_public_key,cert_serial_number,cert_issued_time " +
|
|
"FROM wallet_identifier_generation WHERE user_id=? AND school_id=? AND class_id=? " +
|
|
"AND wallet_identifier=? AND status='CERT_ISSUED' AND deleted=FALSE " +
|
|
"AND cert_private_key IS NOT NULL AND cert_public_key IS NOT NULL " +
|
|
"AND cert_serial_number IS NOT NULL ORDER BY created_at DESC LIMIT 1",
|
|
(rs, row) -> new Certificate(rs.getString("cert_private_key"), rs.getString("cert_public_key"),
|
|
rs.getString("cert_serial_number"), rs.getString("cert_issued_time")),
|
|
subject.getUserId(), subject.getSchoolId(), subject.getClassId(), activation.walletId);
|
|
List<String> rootSignatures = jdbc.query(
|
|
"SELECT cb_root_signature FROM central_wallet_registration " +
|
|
"WHERE user_id=? AND school_id=? AND class_id=? AND wallet_id=? " +
|
|
"AND wallet_registered=TRUE AND sent=TRUE AND cb_root_signature IS NOT NULL " +
|
|
"AND deleted=FALSE ORDER BY created_at DESC LIMIT 1",
|
|
(rs, row) -> rs.getString("cb_root_signature"), subject.getUserId(),
|
|
subject.getSchoolId(), subject.getClassId(), activation.walletId);
|
|
List<Contract> contracts = jdbc.query(
|
|
"SELECT wallet_type,single_payment_limit,daily_payment_limit,annual_payment_limit,balance_ceiling," +
|
|
"contract_plaintext,contract_digest,contract_id,contract_effective_time " +
|
|
"FROM smart_contract_generation WHERE user_id=? AND school_id=? AND class_id=? " +
|
|
"AND wallet_id=? AND sent=TRUE AND status='SENT' AND deleted=FALSE " +
|
|
"AND contract_id IS NOT NULL AND single_payment_limit IS NOT NULL " +
|
|
"AND daily_payment_limit IS NOT NULL AND annual_payment_limit IS NOT NULL " +
|
|
"AND balance_ceiling IS NOT NULL AND contract_plaintext IS NOT NULL " +
|
|
"AND contract_digest IS NOT NULL ORDER BY created_at DESC LIMIT 1",
|
|
(rs, row) -> new Contract(rs.getString("wallet_type"),
|
|
decimal(rs.getString("single_payment_limit")), decimal(rs.getString("daily_payment_limit")),
|
|
decimal(rs.getString("annual_payment_limit")), decimal(rs.getString("balance_ceiling")),
|
|
rs.getString("contract_plaintext"), rs.getString("contract_digest"),
|
|
rs.getString("contract_id"), rs.getString("contract_effective_time")),
|
|
subject.getUserId(), subject.getSchoolId(), subject.getClassId(), activation.walletId);
|
|
if (applications.isEmpty() || certificates.isEmpty() || rootSignatures.isEmpty() || contracts.isEmpty()) {
|
|
throw prerequisite();
|
|
}
|
|
Application application = applications.get(0);
|
|
Contract contract = contracts.get(0);
|
|
if (!blank(application.walletType) && !blank(contract.walletType)
|
|
&& !application.walletType.equals(contract.walletType)) {
|
|
throw conflict("模块三申请钱包类型与合约钱包类型不一致");
|
|
}
|
|
return new SourceWallet(subject.getUserId(), subject.getSchoolId(), subject.getClassId(),
|
|
activation.walletId, activation.finalSignature, timestamp(activation.finalTime), application,
|
|
certificates.get(0), rootSignatures.get(0), contract);
|
|
}
|
|
|
|
private Institution loadInstitution(InstitutionKeySubject subject) {
|
|
List<Institution> institutions = jdbc.query(
|
|
"SELECT bank_code,institution_identifier FROM institution_identifier_application " +
|
|
"WHERE user_id=? AND school_id=? AND class_id=? " +
|
|
"AND status IN ('ISSUED','FEEDBACKED') AND deleted=FALSE " +
|
|
"AND bank_code IS NOT NULL AND institution_identifier IS NOT NULL " +
|
|
"ORDER BY created_at DESC LIMIT 1",
|
|
(rs, row) -> new Institution(rs.getString("bank_code"),
|
|
rs.getString("institution_identifier")), subject.getUserId(),
|
|
subject.getSchoolId(), subject.getClassId());
|
|
if (institutions.isEmpty()) {
|
|
throw new BusinessException(ErrorCode.VALIDATION_ERROR,
|
|
"请先完成机构标识实验并取得当前用户、学校和班级对应的机构标识");
|
|
}
|
|
return institutions.get(0);
|
|
}
|
|
|
|
private String ensureBankAccount(SourceWallet source, Institution institution) {
|
|
String expectedId = "ACCOUNT_" + UUID.nameUUIDFromBytes(
|
|
(source.userId + "|" + institution.bankCode).getBytes(StandardCharsets.UTF_8));
|
|
List<BankAccount> existing = bankAccounts(source.userId, institution.bankCode);
|
|
if (!existing.isEmpty()) {
|
|
validateAccount(existing.get(0), source, institution);
|
|
return existing.get(0).accountId;
|
|
}
|
|
try {
|
|
jdbc.update("INSERT INTO simulated_bank_account " +
|
|
"(account_id,user_id,bank_code,bank_name,card_number,card_last4,balance,frozen_amount,status,created_at,updated_at) " +
|
|
"VALUES (?,?,?,?,?,?,?,0,'ACTIVE',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)",
|
|
expectedId, source.userId, institution.bankCode, source.application.bankName,
|
|
source.application.cardNumber, lastFour(source.application.cardNumber),
|
|
source.application.accountBalance);
|
|
} catch (DuplicateKeyException exception) {
|
|
existing = bankAccounts(source.userId, institution.bankCode);
|
|
if (existing.isEmpty()) throw exception;
|
|
validateAccount(existing.get(0), source, institution);
|
|
return existing.get(0).accountId;
|
|
}
|
|
return expectedId;
|
|
}
|
|
|
|
private void ensureWallet(SourceWallet source) {
|
|
List<Wallet> existing = jdbc.query(
|
|
"SELECT wallet_id,user_id,wallet_type,central_bank_confirmation_signature " +
|
|
"FROM digital_wallet WHERE wallet_id=? OR user_id=?",
|
|
(rs, row) -> new Wallet(rs.getString("wallet_id"), rs.getString("user_id"),
|
|
rs.getString("wallet_type"), rs.getString("central_bank_confirmation_signature")),
|
|
source.walletId, source.userId);
|
|
if (!existing.isEmpty()) {
|
|
if (existing.size() != 1) throw conflict("共享钱包标识或用户归属冲突");
|
|
validateWallet(existing.get(0), source);
|
|
return;
|
|
}
|
|
try {
|
|
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) VALUES (?,?,?,'ACTIVE',0,0,?,?,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)",
|
|
source.walletId, source.userId, source.contract.walletType,
|
|
source.finalSignature, source.openedAt);
|
|
} catch (DuplicateKeyException exception) {
|
|
ensureWallet(source);
|
|
}
|
|
}
|
|
|
|
private void ensureCertificate(SourceWallet source) {
|
|
List<WalletCertificate> existing = jdbc.query(
|
|
"SELECT certificate_serial,wallet_id,public_key,central_bank_root_signature " +
|
|
"FROM wallet_certificate WHERE wallet_id=? OR certificate_serial=?",
|
|
(rs, row) -> new WalletCertificate(rs.getString("certificate_serial"),
|
|
rs.getString("wallet_id"), rs.getString("public_key"),
|
|
rs.getString("central_bank_root_signature")),
|
|
source.walletId, source.certificate.serialNumber);
|
|
if (!existing.isEmpty()) {
|
|
if (existing.size() != 1) throw conflict("共享钱包证书序列号或钱包归属冲突");
|
|
WalletCertificate value = existing.get(0);
|
|
if (!source.walletId.equals(value.walletId)
|
|
|| !source.certificate.serialNumber.equals(value.serialNumber)
|
|
|| !source.certificate.publicKey.equals(value.publicKey)
|
|
|| !source.rootSignature.equals(value.rootSignature)) {
|
|
throw conflict("共享钱包证书与模块三结果不一致");
|
|
}
|
|
return;
|
|
}
|
|
try {
|
|
jdbc.update("INSERT INTO wallet_certificate " +
|
|
"(certificate_serial,wallet_id,public_key,encrypted_private_key,filing_status," +
|
|
"central_bank_root_signature,status,issued_at) VALUES (?,?,?,?,'REGISTERED',?,'VALID',?)",
|
|
source.certificate.serialNumber, source.walletId, source.certificate.publicKey,
|
|
privateKeyCipher.encrypt(source.certificate.privateKey), source.rootSignature,
|
|
timestamp(source.certificate.issuedTime));
|
|
} catch (DuplicateKeyException exception) {
|
|
ensureCertificate(source);
|
|
}
|
|
}
|
|
|
|
private void ensureContract(SourceWallet source) {
|
|
List<WalletContract> existing = jdbc.query(
|
|
"SELECT contract_id,wallet_id,wallet_type FROM wallet_contract WHERE wallet_id=? OR contract_id=?",
|
|
(rs, row) -> new WalletContract(rs.getString("contract_id"),
|
|
rs.getString("wallet_id"), rs.getString("wallet_type")),
|
|
source.walletId, source.contract.contractId);
|
|
if (!existing.isEmpty()) {
|
|
if (existing.size() != 1) throw conflict("共享钱包合约标识或钱包归属冲突");
|
|
WalletContract value = existing.get(0);
|
|
if (!source.walletId.equals(value.walletId)
|
|
|| !source.contract.contractId.equals(value.contractId)
|
|
|| !source.contract.walletType.equals(value.walletType)) {
|
|
throw conflict("共享钱包合约与模块三结果不一致");
|
|
}
|
|
return;
|
|
}
|
|
try {
|
|
jdbc.update("INSERT INTO wallet_contract " +
|
|
"(contract_id,wallet_id,wallet_type,single_payment_limit,daily_payment_limit," +
|
|
"annual_payment_limit,balance_limit,valid_until,original_text,digest,status,daily_used_amount," +
|
|
"daily_counter_date,annual_used_amount,annual_counter_year,created_at,updated_at) " +
|
|
"VALUES (?,?,?,?,?,?,?,NULL,?,?,'ACTIVE',0,NULL,0,NULL,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)",
|
|
source.contract.contractId, source.walletId, source.contract.walletType,
|
|
source.contract.singleLimit, source.contract.dailyLimit, source.contract.annualLimit,
|
|
source.contract.balanceLimit, source.contract.originalText, source.contract.digest);
|
|
} catch (DuplicateKeyException exception) {
|
|
ensureContract(source);
|
|
}
|
|
}
|
|
|
|
private void ensureBinding(String walletId, String accountId) {
|
|
List<String> existing = jdbc.query(
|
|
"SELECT bank_account_id FROM wallet_bank_binding WHERE wallet_id=?",
|
|
(rs, row) -> rs.getString("bank_account_id"), walletId);
|
|
if (!existing.isEmpty()) {
|
|
if (existing.size() != 1 || !accountId.equals(existing.get(0))) {
|
|
throw conflict("共享钱包银行卡绑定与模块三结果不一致");
|
|
}
|
|
return;
|
|
}
|
|
try {
|
|
jdbc.update("INSERT INTO wallet_bank_binding (wallet_id,bank_account_id,status,bound_at) " +
|
|
"VALUES (?,?,'BOUND',CURRENT_TIMESTAMP)", walletId, accountId);
|
|
} catch (DuplicateKeyException exception) {
|
|
ensureBinding(walletId, accountId);
|
|
}
|
|
}
|
|
|
|
private List<BankAccount> bankAccounts(String userId, String bankCode) {
|
|
return jdbc.query("SELECT account_id,user_id,bank_code,card_number FROM simulated_bank_account " +
|
|
"WHERE user_id=? AND bank_code=?",
|
|
(rs, row) -> new BankAccount(rs.getString("account_id"), rs.getString("user_id"),
|
|
rs.getString("bank_code"), rs.getString("card_number")), userId, bankCode);
|
|
}
|
|
|
|
private void validateAccount(BankAccount value, SourceWallet source, Institution institution) {
|
|
if (!source.userId.equals(value.userId) || !institution.bankCode.equals(value.bankCode)
|
|
|| !source.application.cardNumber.equals(value.cardNumber)) {
|
|
throw conflict("共享银行账户与模块三银行卡结果不一致");
|
|
}
|
|
}
|
|
|
|
private void validateWallet(Wallet value, SourceWallet source) {
|
|
if (!source.walletId.equals(value.walletId) || !source.userId.equals(value.userId)
|
|
|| !source.contract.walletType.equals(value.walletType)
|
|
|| !source.finalSignature.equals(value.finalSignature)) {
|
|
throw conflict("共享钱包与模块三最终结果不一致");
|
|
}
|
|
}
|
|
|
|
private Timestamp timestamp(String value) {
|
|
if (!blank(value)) {
|
|
try {
|
|
return Timestamp.valueOf(LocalDateTime.parse(value.trim(), MODULE_TIMESTAMP));
|
|
} catch (DateTimeParseException ignored) {
|
|
// Module 3 historical rows may use display text; initialization time is the safe fallback.
|
|
}
|
|
}
|
|
return Timestamp.valueOf(LocalDateTime.now());
|
|
}
|
|
|
|
private BigDecimal decimal(String value) {
|
|
try {
|
|
return new BigDecimal(value).setScale(2);
|
|
} catch (RuntimeException exception) {
|
|
throw prerequisite();
|
|
}
|
|
}
|
|
|
|
private String lastFour(String cardNumber) {
|
|
if (blank(cardNumber) || cardNumber.trim().length() < 4) throw prerequisite();
|
|
String normalized = cardNumber.trim();
|
|
return normalized.substring(normalized.length() - 4);
|
|
}
|
|
|
|
private boolean blank(String value) {
|
|
return value == null || value.trim().isEmpty();
|
|
}
|
|
|
|
private BusinessException prerequisite() {
|
|
return new BusinessException(ErrorCode.VALIDATION_ERROR,
|
|
"请先完成个人数字钱包开通实验的申请、证书备案、合约生成和央行最终确认");
|
|
}
|
|
|
|
private BusinessException conflict(String message) {
|
|
return new BusinessException(ErrorCode.VALIDATION_ERROR, message);
|
|
}
|
|
|
|
private static final class Activation {
|
|
private final String walletId;
|
|
private final String finalSignature;
|
|
private final String finalTime;
|
|
private Activation(String walletId, String finalSignature, String finalTime) {
|
|
this.walletId = walletId; this.finalSignature = finalSignature; this.finalTime = finalTime;
|
|
}
|
|
}
|
|
|
|
private static final class Application {
|
|
private final String bankName;
|
|
private final String cardNumber;
|
|
private final BigDecimal accountBalance;
|
|
private final String walletType;
|
|
private Application(String bankName, String cardNumber, BigDecimal accountBalance, String walletType) {
|
|
this.bankName = bankName; this.cardNumber = cardNumber;
|
|
this.accountBalance = accountBalance; this.walletType = walletType;
|
|
}
|
|
}
|
|
|
|
private static final class Certificate {
|
|
private final String privateKey;
|
|
private final String publicKey;
|
|
private final String serialNumber;
|
|
private final String issuedTime;
|
|
private Certificate(String privateKey, String publicKey, String serialNumber, String issuedTime) {
|
|
this.privateKey = privateKey; this.publicKey = publicKey;
|
|
this.serialNumber = serialNumber; this.issuedTime = issuedTime;
|
|
}
|
|
}
|
|
|
|
private static final class Contract {
|
|
private final String walletType;
|
|
private final BigDecimal singleLimit;
|
|
private final BigDecimal dailyLimit;
|
|
private final BigDecimal annualLimit;
|
|
private final BigDecimal balanceLimit;
|
|
private final String originalText;
|
|
private final String digest;
|
|
private final String contractId;
|
|
private final String effectiveTime;
|
|
private Contract(String walletType, BigDecimal singleLimit, BigDecimal dailyLimit,
|
|
BigDecimal annualLimit, BigDecimal balanceLimit, String originalText,
|
|
String digest, String contractId, String effectiveTime) {
|
|
this.walletType = walletType; this.singleLimit = singleLimit; this.dailyLimit = dailyLimit;
|
|
this.annualLimit = annualLimit; this.balanceLimit = balanceLimit; this.originalText = originalText;
|
|
this.digest = digest; this.contractId = contractId; this.effectiveTime = effectiveTime;
|
|
}
|
|
}
|
|
|
|
private static final class SourceWallet {
|
|
private final String userId;
|
|
private final long schoolId;
|
|
private final long classId;
|
|
private final String walletId;
|
|
private final String finalSignature;
|
|
private final Timestamp openedAt;
|
|
private final Application application;
|
|
private final Certificate certificate;
|
|
private final String rootSignature;
|
|
private final Contract contract;
|
|
private SourceWallet(String userId, long schoolId, long classId, String walletId,
|
|
String finalSignature, Timestamp openedAt, Application application,
|
|
Certificate certificate, String rootSignature, Contract contract) {
|
|
this.userId = userId; this.schoolId = schoolId; this.classId = classId;
|
|
this.walletId = walletId; this.finalSignature = finalSignature; this.openedAt = openedAt;
|
|
this.application = application; this.certificate = certificate;
|
|
this.rootSignature = rootSignature; this.contract = contract;
|
|
}
|
|
}
|
|
|
|
private static final class Institution {
|
|
private final String bankCode;
|
|
private final String organizationId;
|
|
private Institution(String bankCode, String organizationId) {
|
|
this.bankCode = bankCode; this.organizationId = organizationId;
|
|
}
|
|
}
|
|
|
|
private static final class BankAccount {
|
|
private final String accountId;
|
|
private final String userId;
|
|
private final String bankCode;
|
|
private final String cardNumber;
|
|
private BankAccount(String accountId, String userId, String bankCode, String cardNumber) {
|
|
this.accountId = accountId; this.userId = userId;
|
|
this.bankCode = bankCode; this.cardNumber = cardNumber;
|
|
}
|
|
}
|
|
|
|
private static final class Wallet {
|
|
private final String walletId;
|
|
private final String userId;
|
|
private final String walletType;
|
|
private final String finalSignature;
|
|
private Wallet(String walletId, String userId, String walletType, String finalSignature) {
|
|
this.walletId = walletId; this.userId = userId;
|
|
this.walletType = walletType; this.finalSignature = finalSignature;
|
|
}
|
|
}
|
|
|
|
private static final class WalletCertificate {
|
|
private final String serialNumber;
|
|
private final String walletId;
|
|
private final String publicKey;
|
|
private final String rootSignature;
|
|
private WalletCertificate(String serialNumber, String walletId, String publicKey, String rootSignature) {
|
|
this.serialNumber = serialNumber; this.walletId = walletId;
|
|
this.publicKey = publicKey; this.rootSignature = rootSignature;
|
|
}
|
|
}
|
|
|
|
private static final class WalletContract {
|
|
private final String contractId;
|
|
private final String walletId;
|
|
private final String walletType;
|
|
private WalletContract(String contractId, String walletId, String walletType) {
|
|
this.contractId = contractId; this.walletId = walletId; this.walletType = walletType;
|
|
}
|
|
}
|
|
}
|