Search in sources :

Example 1 with Currency

use of com.whoiszxl.entity.Currency in project shopzz by whoiszxl.

the class BtcScanTask method scanOrder.

/**
 * 扫描链上的交易是否和数据库中的支付单是否匹配,如果匹配则修改对应状态。
 * 在最近的300个区块的出块时间一般平均为15秒。
 * 定时任务使用10秒间隔(10 * 1000)。
 * https://txstreet.com/
 */
@Scheduled(fixedDelay = 60 * 1000)
public void scanOrder() {
    // 1. 获取当前货币的配置信息
    Currency btcInfo = currencyService.getCurrencyByName(currencyName);
    AssertUtils.isNotNull(btcInfo, "数据库未配置货币信息:" + currencyName);
    // 2. 获取到当前与网络区块高度
    // 获取到当前与网络区块高度
    int networkBlockHeight = bitcoinClient.getBlockCount();
    Height heightObj = heightService.getHeightByCurrencyName(currencyName);
    if (heightObj == null) {
        Height height = new Height();
        height.setCurrencyId(btcInfo.getId());
        height.setCurrencyName(btcInfo.getCurrencyName());
        height.setCurrentHeight((long) networkBlockHeight);
        heightService.save(height);
        return;
    }
    int currentHeight = heightObj.getCurrentHeight().intValue();
    // 3. 相隔1个区块不进行扫描
    if (networkBlockHeight - currentHeight <= 1) {
        log.info("相隔1个区块不进行扫描");
        return;
    }
    // 4. 遍历扫描区块中的交易
    for (Integer i = currentHeight + 1; i <= networkBlockHeight; i++) {
        // 通过区块高度拿到区块Hash,再通过区块Hash拿到区块对象,再从区块对象中拿到交易ID集合
        String blockHash = bitcoinClient.getBlockHash(i);
        BitcoindRpcClient.Block block = bitcoinClient.getBlock(blockHash);
        List<String> txs = block.tx();
        // 遍历区块中的所有交易,判断是否在咱们的数据库中
        for (String txId : txs) {
            // 通过交易ID获取到交易对象,从交易对象中拿到交易输出,交易输出就是交易的收款方信息。
            BitcoindRpcClient.RawTransaction transaction = bitcoinClient.getRawTransaction(txId);
            List<BitcoindRpcClient.RawTransaction.Out> outs = transaction.vOut();
            // 判断交易输出集是否有效
            if (CollectionUtils.isEmpty(outs)) {
                continue;
            }
            // 遍历交易输出集
            for (BitcoindRpcClient.RawTransaction.Out out : outs) {
                // 判断公钥脚本是否有效
                if (out.scriptPubKey() == null) {
                    continue;
                }
                // 拿到地址在数据库判断记录是否存在
                if (CollectionUtils.isEmpty(out.scriptPubKey().addresses())) {
                    continue;
                }
                String address = out.scriptPubKey().addresses().get(0);
                PayInfoDc payInfoDc = dcPayInfoService.getRechargeByAddressAndCurrencyNameAndUpchainStatus(address, currencyName, UpchainStatusEnum.NOT_UPCHAIN.getCode());
                if (payInfoDc == null) {
                    // log.info("{} 地址不在库中:{}", currencyName, transaction.getTo());
                    continue;
                }
                if (out.value().compareTo(payInfoDc.getTotalAmount()) < 0) {
                    log.info("链上充值金额小于订单金额, 订单金额为:{}, 链上充值金额为:{}", payInfoDc.getTotalAmount().toPlainString(), out.value().toPlainString());
                    continue;
                }
                // 更新recharge表
                payInfoDc.setTxHash(txId);
                payInfoDc.setCurrentConfirm(transaction.confirmations().longValue());
                payInfoDc.setHeight(i.longValue());
                payInfoDc.setUpdatedAt(new Date());
                payInfoDc.setUpchainAt(block.time());
                if (block.confirmations() >= btcInfo.getConfirms()) {
                    payInfoDc.setUpchainStatus(UpchainStatusEnum.SUCCESS.getCode());
                    payInfoDc.setUpchainSuccessAt(transaction.time());
                } else {
                    payInfoDc.setUpchainStatus(UpchainStatusEnum.WAITING_CONFIRM.getCode());
                }
                dcPayInfoService.updateById(payInfoDc);
            }
        }
        // 12. 更新区块高度
        heightObj.setCurrentHeight((long) networkBlockHeight);
        heightService.updateById(heightObj);
    }
}
Also used : PayInfoDc(com.whoiszxl.entity.PayInfoDc) BitcoindRpcClient(wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient) Date(java.util.Date) Currency(com.whoiszxl.entity.Currency) Height(com.whoiszxl.entity.Height) Scheduled(org.springframework.scheduling.annotation.Scheduled)

Example 2 with Currency

use of com.whoiszxl.entity.Currency in project shopzz by whoiszxl.

the class ShopzzFeignClientImpl method giveAddress.

@Override
@Transactional
public ResponseResult<RechargeResponse> giveAddress(String orderId, String amount) {
    // 1. 获取货币信息
    Currency ethInfo = currencyService.getCurrencyByName(currencyName);
    AssertUtils.isNotNull(ethInfo, "数据库未配置货币信息:" + currencyName);
    // 2. 通过keystore的方式生成一个以太坊地址
    EthereumAddress ethereumAddress = ethereumService.createAddressByFile();
    String qrCodeData = getQrCodeData(amount, ethereumAddress.getAddress());
    // 3. 保存keystore文件与地址的对应关系
    CurrencyAccount account = new CurrencyAccount();
    account.setAddress(ethereumAddress.getAddress());
    account.setCurrencyId(ethInfo.getId());
    account.setCurrencyName(ethInfo.getCurrencyName());
    account.setKeystoreName(ethereumAddress.getKeystoreName());
    account.setMnemonic(ethereumAddress.getMnemonic());
    currencyAccountService.save(account);
    return ResponseResult.buildSuccess(new RechargeResponse(ethInfo.getId(), ethereumAddress.getAddress(), qrCodeData));
}
Also used : EthereumAddress(com.whoiszxl.common.EthereumAddress) CurrencyAccount(com.whoiszxl.entity.CurrencyAccount) RechargeResponse(com.whoiszxl.entity.response.RechargeResponse) Currency(com.whoiszxl.entity.Currency) Transactional(org.springframework.transaction.annotation.Transactional)

Example 3 with Currency

use of com.whoiszxl.entity.Currency in project shopzz by whoiszxl.

the class Erc20ScanTask method confirmTx.

/**
 * 确认交易,将数据库中状态为待确认的充值单再次去链上查询是否确认数超过了配置确认数。
 * 在最近的300个区块的出块时间一般平均为15秒。
 * 定时任务使用15秒间隔(15 * 1000)。
 * https://txstreet.com/
 */
@Scheduled(fixedDelay = 10 * 1000)
public void confirmTx() {
    // 0. 获取当前货币的配置信息
    Currency tokenInfo = currencyService.getCurrencyByName(currencyName);
    AssertUtils.isNotNull(tokenInfo, "数据库未配置货币信息:" + currencyName);
    // 1. 获取当前网络的区块高度
    Long currentHeight = ethereumService.getBlockchainHeight();
    // 2. 查询到所有待确认的充值单
    List<PayInfoDc> waitConfirmPayInfo = dcPayInfoService.getWaitConfirmPayInfo(currencyName);
    if (waitConfirmPayInfo == null) {
        return;
    }
    // 3. 遍历库中交易进行判断是否成功
    List<Long> paySuccessOrderIds = new ArrayList<>();
    for (PayInfoDc payInfoDc : waitConfirmPayInfo) {
        Transaction transaction = ethereumService.getTransactionByHash(payInfoDc.getTxHash());
        // 如果链上交易确认数大于等于配置的确认数,则更新充值单为成功并更新上链成功时间,否则只更新当前确认数。
        if (currentHeight - transaction.getBlockNumber().longValue() >= tokenInfo.getConfirms()) {
            payInfoDc.setUpchainStatus(UpchainStatusEnum.SUCCESS.getCode());
            payInfoDc.setUpchainSuccessAt(new Date());
        }
        payInfoDc.setCurrentConfirm(currentHeight - transaction.getBlockNumber().longValue());
        payInfoDc.setUpdatedAt(new Date());
        dcPayInfoService.updateById(payInfoDc);
        paySuccessOrderIds.add(payInfoDc.getOrderId());
    }
    if (ObjectUtils.isNotEmpty(paySuccessOrderIds)) {
        // 成功了发送消息去更新oms_order记录为支付成功
        ResponseResult<Boolean> responseResult = orderFeignClient.notifyDcPaySuccess(paySuccessOrderIds);
        if (!responseResult.isOk()) {
            log.error("支付成功了,但是更新订单未成功", responseResult.getMessage());
        }
    }
}
Also used : PayInfoDc(com.whoiszxl.entity.PayInfoDc) Transaction(org.web3j.protocol.core.methods.response.Transaction) Currency(com.whoiszxl.entity.Currency) Scheduled(org.springframework.scheduling.annotation.Scheduled)

Example 4 with Currency

use of com.whoiszxl.entity.Currency in project shopzz by whoiszxl.

the class Erc20ScanTask method scanOrder.

/**
 * 扫描链上的交易是否和数据库中的充值单是否匹配,如果匹配则修改对应状态。
 * 在最近的300个区块的出块时间一般平均为15秒。
 * 定时任务使用10秒间隔(10 * 1000)。
 * https://txstreet.com/
 */
@Scheduled(fixedDelay = 10 * 1000)
public void scanOrder() {
    // 获取当前货币的配置信息
    Currency tokenInfo = currencyService.getCurrencyByName(currencyName);
    AssertUtils.isNotNull(tokenInfo, "数据库未配置货币信息:" + currencyName);
    // 获取到当前与网络区块高度
    Long networkBlockHeight = ethereumService.getBlockchainHeight();
    Height heightObj = heightService.getHeightByCurrencyName(currencyName);
    if (heightObj == null) {
        Height height = new Height();
        height.setCurrencyId(tokenInfo.getId());
        height.setCurrencyName(tokenInfo.getCurrencyName());
        height.setCurrentHeight(networkBlockHeight);
        heightService.save(height);
        return;
    }
    Long currentHeight = heightObj.getCurrentHeight();
    // 3. 相隔1个区块不进行扫描
    if (networkBlockHeight - currentHeight <= 1) {
        return;
    }
    // 扫描区块中的交易
    for (Long i = currentHeight + 1; i <= networkBlockHeight; i++) {
        log.info("开始扫描区块:{}", i);
        // 通过区块高度获取到区块中的交易信息
        EthBlock.Block block = ethereumService.getBlockByNumber(i);
        List<EthBlock.TransactionResult> transactionResults = block.getTransactions();
        for (EthBlock.TransactionResult transactionResult : transactionResults) {
            EthBlock.TransactionObject transactionObject = (EthBlock.TransactionObject) transactionResult;
            Transaction transaction = transactionObject.get();
            // 通过交易Hash获取到交易的回执信息
            TransactionReceipt txReceipt = ethereumService.getTransactionReceipt(transaction.getHash());
            if (txReceipt == null) {
                continue;
            }
            // 判断状态是否是成功(1成功 0失败)
            if (txReceipt.getStatus().equalsIgnoreCase("0x1")) {
                String input = transaction.getInput();
                String toContractAddress = transaction.getTo();
                if (!StringUtils.isEmpty(input) && input.length() >= 138 && tokenInfo.getContractAddress().equalsIgnoreCase(toContractAddress)) {
                    String data = input.substring(0, 9);
                    data = data + input.substring(17);
                    Function function = new Function("transfer", Collections.emptyList(), Arrays.asList(new TypeReference<Address>() {
                    }, new TypeReference<Uint256>() {
                    }));
                    List<Type> params = FunctionReturnDecoder.decode(data, function.getOutputParameters());
                    // 获取充币地址和金额
                    String toAddress = params.get(0).getValue().toString();
                    String amount = params.get(1).getValue().toString();
                    BigDecimal amountDecimal = new BigDecimal(amount).movePointLeft(tokenInfo.getCurrencyDecimalsNum());
                    PayInfoDc payInfoDc = dcPayInfoService.getRechargeByAddressAndCurrencyNameAndUpchainStatus(toAddress, currencyName, UpchainStatusEnum.NOT_UPCHAIN.getCode());
                    if (payInfoDc == null) {
                        log.info("{} 地址不在库中:{}", currencyName, transaction.getTo());
                        continue;
                    }
                    if (amountDecimal.compareTo(payInfoDc.getTotalAmount()) < 0) {
                        log.info("链上充值金额小于订单金额, 订单金额为:{}, 链上充值金额为:{}", payInfoDc.getTotalAmount().toPlainString(), amountDecimal.toPlainString());
                        continue;
                    }
                    // 判断是否是假充值
                    boolean flag = ethereumService.checkEventLog(i.intValue(), tokenInfo.getContractAddress(), transaction.getHash());
                    if (!flag) {
                        continue;
                    }
                    payInfoDc.setFromAddress(transaction.getFrom());
                    payInfoDc.setTxHash(transaction.getHash());
                    payInfoDc.setCurrentConfirm(transaction.getBlockNumber().subtract(BigInteger.valueOf(i)).longValue());
                    payInfoDc.setHeight(transaction.getBlockNumber().longValue());
                    payInfoDc.setUpchainAt(new Date(block.getTimestamp().longValue()));
                    payInfoDc.setUpdatedAt(new Date());
                    if (i - block.getNumber().intValue() >= tokenInfo.getConfirms()) {
                        payInfoDc.setUpchainStatus(UpchainStatusEnum.SUCCESS.getCode());
                        payInfoDc.setUpchainSuccessAt(new Date(block.getTimestamp().longValue()));
                    } else {
                        payInfoDc.setUpchainStatus(UpchainStatusEnum.WAITING_CONFIRM.getCode());
                    }
                    dcPayInfoService.updateById(payInfoDc);
                }
            }
        }
    }
    // 更新区块高度
    heightObj.setCurrentHeight(networkBlockHeight);
    heightObj.setUpdatedAt(new Date());
    heightService.updateById(heightObj);
}
Also used : PayInfoDc(com.whoiszxl.entity.PayInfoDc) EthBlock(org.web3j.protocol.core.methods.response.EthBlock) TransactionReceipt(org.web3j.protocol.core.methods.response.TransactionReceipt) BigDecimal(java.math.BigDecimal) Function(org.web3j.abi.datatypes.Function) Type(org.web3j.abi.datatypes.Type) Transaction(org.web3j.protocol.core.methods.response.Transaction) Currency(com.whoiszxl.entity.Currency) Height(com.whoiszxl.entity.Height) TypeReference(org.web3j.abi.TypeReference) Scheduled(org.springframework.scheduling.annotation.Scheduled)

Example 5 with Currency

use of com.whoiszxl.entity.Currency in project shopzz by whoiszxl.

the class EthFeignClientImpl method giveAddress.

@Override
@Transactional
@PostMapping("/createRecharge/{orderId}/{amount}")
public ResponseResult<RechargeResponse> giveAddress(String orderId, String amount) {
    // 1. 获取货币信息
    Currency ethInfo = currencyService.getCurrencyByName(currencyName);
    AssertUtils.isNotNull(ethInfo, "数据库未配置货币信息:" + currencyName);
    // 2. 通过keystore的方式生成一个以太坊地址
    EthereumAddress ethereumAddress = ethereumService.createAddressByFile();
    String qrCodeData = getQrCodeData(amount, ethereumAddress.getAddress());
    // 3. 保存keystore文件与地址的对应关系
    CurrencyAccount account = new CurrencyAccount();
    account.setAddress(ethereumAddress.getAddress());
    account.setCurrencyId(ethInfo.getId());
    account.setCurrencyName(ethInfo.getCurrencyName());
    account.setKeystoreName(ethereumAddress.getKeystoreName());
    account.setMnemonic(ethereumAddress.getMnemonic());
    currencyAccountService.save(account);
    return ResponseResult.buildSuccess(new RechargeResponse(ethInfo.getId(), ethereumAddress.getAddress(), qrCodeData));
}
Also used : EthereumAddress(com.whoiszxl.common.EthereumAddress) CurrencyAccount(com.whoiszxl.entity.CurrencyAccount) RechargeResponse(com.whoiszxl.entity.response.RechargeResponse) Currency(com.whoiszxl.entity.Currency) PostMapping(org.springframework.web.bind.annotation.PostMapping) Transactional(org.springframework.transaction.annotation.Transactional)

Aggregations

Currency (com.whoiszxl.entity.Currency)10 PayInfoDc (com.whoiszxl.entity.PayInfoDc)6 Scheduled (org.springframework.scheduling.annotation.Scheduled)6 RechargeResponse (com.whoiszxl.entity.response.RechargeResponse)4 Date (java.util.Date)4 Transactional (org.springframework.transaction.annotation.Transactional)4 Transaction (org.web3j.protocol.core.methods.response.Transaction)4 EthereumAddress (com.whoiszxl.common.EthereumAddress)3 CurrencyAccount (com.whoiszxl.entity.CurrencyAccount)3 Height (com.whoiszxl.entity.Height)3 BigDecimal (java.math.BigDecimal)2 ArrayList (java.util.ArrayList)2 PostMapping (org.springframework.web.bind.annotation.PostMapping)2 EthBlock (org.web3j.protocol.core.methods.response.EthBlock)2 BitcoindRpcClient (wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient)2 TypeReference (org.web3j.abi.TypeReference)1 Function (org.web3j.abi.datatypes.Function)1 Type (org.web3j.abi.datatypes.Type)1 TransactionReceipt (org.web3j.protocol.core.methods.response.TransactionReceipt)1