ATO Report,

order channel support
master
yixian 6 years ago
parent 615b9ddda0
commit 8321cfb724

@ -36,6 +36,12 @@
<groupId>au.com.royalpay.payment</groupId>
<artifactId>jd-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.stuxuhai</groupId>
<artifactId>jpinyin</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>au.com.royalpay.payment</groupId>
<artifactId>bestpay-core</artifactId>

@ -0,0 +1,93 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Create by yixian at 2018-08-30 18:38
*/
public class ATOBulkData {
private IntermediaryData intermediary;
public ATOBulkData(IntermediaryData intermediary) {
this.intermediary = intermediary;
}
public String outputBTTPS() {
StringBuilder builder = new StringBuilder();
intermediary.buildLine(builder);
return builder.toString();
}
public static class IntermediaryData implements ATOBulkLine {
private final String identifier = "IDENTREGISTER";
private final String version = "BTPSV001.0";
private String abn;
private Date createTime;
private String fileReference;
private String name;
private ContactInfo contactInfo;
private AddressInfo address;
private List<ReportingPartyData> reportingParties = new ArrayList<>();
public IntermediaryData(String abn, String name, String fileReference) {
this.abn = abn;
this.fileReference = fileReference;
this.name = name;
this.createTime = new Date();
}
public IntermediaryData setContactInfo(ContactInfo contactInfo) {
this.contactInfo = contactInfo;
return this;
}
public IntermediaryData setAddress(AddressInfo address) {
this.address = address;
return this;
}
@Override
public StringBuilder buildLine(StringBuilder builder) {
StringBuilder line = new StringBuilder();
appendNParam(line, 1400, 4);
appendAParam(line, identifier, 13);
appendAParam(line, version, 10);
appendAParam(line, abn, 11);
appendDTParam(line, createTime, 28);
appendAParam(line, fileReference, 16);
appendAParam(line, name, 200);
appendAParam(line, contactInfo.getContactName(), 40);
appendNParam(line, contactInfo.getPhoneAreaCode(), 2);
appendAParam(line, contactInfo.getPhoneNumber(), 15);
appendAParam(line, address.getAddress(), 38 * 2);
appendAParam(line, address.getSuburb(), 27);
appendAParam(line, address.getState(), 3);
appendNParam(line, address.getPostCode(), 4);
appendAParam(line, address.getCountry(), 50);
appendAParam(line, contactInfo.getEmail(), 76);
appendFiller(line, 825);
builder.append(line);
for (ReportingPartyData partyData : reportingParties) {
partyData.buildLine(builder);
}
TotalDataRecord total = new TotalDataRecord()
.setReportPartyCount(reportingParties.size())
.setBusinessCount(reportingParties.stream().mapToInt(ReportingPartyData::businessCount).sum())
.setTransactionsCount(reportingParties.stream().mapToInt(ReportingPartyData::transactionCount).sum());
total.buildLine(builder);
return builder;
}
public void addReportingParty(ReportingPartyData reportingParty) {
reportingParties.add(reportingParty);
}
}
}

@ -0,0 +1,70 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
import com.github.stuxuhai.jpinyin.PinyinException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.util.Date;
/**
* Create by yixian at 2018-08-30 21:11
*/
public interface ATOBulkLine {
StringBuilder buildLine(StringBuilder builder);
default StringBuilder appendAParam(StringBuilder line, String content, int length) {
content = content == null ? "" : StringUtils.trim(content);
content = StringUtils.remove(StringUtils.remove(content, '\r'), '\n');
int contentCharacterLength = content.getBytes().length;
int contentLength = content.length();
if (contentCharacterLength != contentLength) {
try {
content = CharacterUtils.convertToAlphaBet(content);
} catch (PinyinException e) {
e.printStackTrace();
content = "";
}
}
content = content.replace("?", " ");
if (content.getBytes().length != content.length()) {
content = "";
}
line.append(StringUtils.left(StringUtils.rightPad(content, length, " "), length));
return line;
}
default StringBuilder appendDParam(StringBuilder line, Date date, int len) {
String dateStr = DateFormatUtils.format(date, "yyyyMMdd");
return appendAParam(line, dateStr, len);
}
default StringBuilder appendDTParam(StringBuilder line, Date date, int len) {
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSZZ");
String dateStr = formatter.print(new DateTime(date));
return appendAParam(line, dateStr, len);
}
default StringBuilder appendNParam(StringBuilder line, int num, int len) {
line.append(StringUtils.right(StringUtils.leftPad(num + "", len, "0"), len));
return line;
}
default StringBuilder appendNParam(StringBuilder line, String num, int len) {
num = num == null ? "" : StringUtils.trim(num);
line.append(StringUtils.right(StringUtils.leftPad(num, len, "0"), len));
return line;
}
default StringBuilder appendNSParam(StringBuilder line, int num, int len) {
line.append(StringUtils.right(StringUtils.leftPad(num + "", len, " "), len));
return line;
}
default StringBuilder appendFiller(StringBuilder line, int len) {
line.append(StringUtils.repeat(" ", len)).append("\r\n");
return line;
}
}

@ -0,0 +1,47 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
/**
* Create by yixian at 2018-08-30 21:17
*/
public class AddressInfo {
private String address;
private String suburb;
private String state;
private String postCode;
private String country;
public AddressInfo(String address, String suburb, String state, String postCode, String country) {
this.address = address;
this.suburb = suburb;
this.state = state;
if (!postCode.matches("^\\d{4}$")) {
postCode = "9999";
country = "OTH";
}
this.postCode = postCode;
if (country != null && country.length() != 3) {
country = "OTH";
}
this.country = country;
}
public String getAddress() {
return address;
}
public String getSuburb() {
return suburb;
}
public String getState() {
return state;
}
public String getPostCode() {
return postCode;
}
public String getCountry() {
return "AUS".equals(country) ? "" : country;
}
}

@ -0,0 +1,121 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Create by yixian at 2018-08-30 21:21
*/
public class BusinessData implements ATOBulkLine {
private final String identifier = "BIDR";
private String customer;
private Date paymentFacility;
private String surName;
private String firstName;
private String secondName;
private Date birth;
private String abn;
private String acn;
private String registeredName;
private String tradingName;
private AddressInfo address;
private AddressInfo postalAddress;
private ContactInfo contactInfo;
private String mchId;
private String category;
private String lodgment;
public BusinessData(Date paymentFacility, String abn, String acn, String registeredName, String tradingName, String mchId, String category, String lodgment) {
this.paymentFacility = paymentFacility;
if (abn != null) {
abn = abn.replaceAll("\\D", "");
if (abn.length() != 11) {
abn = "";
}
}
this.abn = abn;
if (acn != null) {
acn = acn.replaceAll("\\D", "");
if (acn.length() != 9) {
acn = "";
}
}
this.acn = acn;
this.registeredName = registeredName;
this.tradingName = tradingName;
this.mchId = mchId;
this.customer = mchId;
this.category = category;
this.lodgment = lodgment;
}
public BusinessData setAddress(AddressInfo address) {
this.address = address;
return this;
}
public BusinessData setPostalAddress(AddressInfo postalAddress) {
this.postalAddress = postalAddress;
return this;
}
public BusinessData setContactInfo(ContactInfo contactInfo) {
this.contactInfo = contactInfo;
return this;
}
private List<TransactionSummaryData> transactions = new ArrayList<>();
@Override
public StringBuilder buildLine(StringBuilder builder) {
StringBuilder line = new StringBuilder();
appendNParam(line, 1400, 4);
appendAParam(line, identifier, 4);
appendAParam(line, customer, 25);
appendDParam(line, paymentFacility, 8);
appendAParam(line, surName, 40);
appendAParam(line, firstName, 40);
appendAParam(line, secondName, 40);
appendAParam(line, null, 8);
appendNParam(line, abn, 11);
appendNParam(line, acn, 9);
appendAParam(line, registeredName, 200);
appendAParam(line, tradingName, 200);
appendAParam(line, address.getAddress(), 38 * 2);
appendAParam(line, address.getSuburb(), 27);
appendAParam(line, address.getState(), 3);
appendNParam(line, address.getPostCode(), 4);
appendAParam(line, address.getCountry(), 50);
appendAParam(line, postalAddress.getAddress(), 38 * 2);
appendAParam(line, postalAddress.getSuburb(), 27);
appendAParam(line, postalAddress.getState(), 3);
appendNParam(line, postalAddress.getPostCode(), 4);
appendAParam(line, postalAddress.getCountry(), 50);
appendAParam(line, contactInfo.getContactName(), 40);
appendNParam(line, contactInfo.getPhoneAreaCode(), 2);
appendAParam(line, contactInfo.getPhoneNumber(), 15);
appendAParam(line, contactInfo.getEmail(), 76);
appendAParam(line, mchId, 20);
appendAParam(line, category, 100);
appendAParam(line, lodgment, 1);
appendFiller(line, 237);
builder.append(line);
for (TransactionSummaryData trans : transactions) {
trans.buildLine(builder);
}
return builder;
}
public int transactionCount() {
return transactions.size();
}
public void addTransaction(TransactionSummaryData transactionSummaryData) {
//todo system account number
transactionSummaryData.fillSystemInfo(mchId, "Merchant Acquiring System", mchId);
transactions.add(transactionSummaryData);
}
}

@ -0,0 +1,53 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
import com.github.stuxuhai.jpinyin.PinyinException;
import com.github.stuxuhai.jpinyin.PinyinFormat;
import com.github.stuxuhai.jpinyin.PinyinHelper;
import org.apache.commons.lang3.StringUtils;
import java.util.stream.Collectors;
/**
* Create by yixian at 2018-09-06 9:03
*/
public class CharacterUtils {
public static String convertToAlphaBet(String source) throws PinyinException {
return convertFullWidthToHalfWidth(convertToPinYin(source));
}
public static String convertToPinYin(String source) throws PinyinException {
return PinyinHelper.convertToPinyinString(source, "", PinyinFormat.WITHOUT_TONE).toUpperCase();
}
public static String convertFullWidthToHalfWidth(String source) {
if (StringUtils.isEmpty(source)) {
return source;
}
return source.chars().map(ch -> {
if (ch == '\u3000') {
return '\u0020';
}
if (isFullWidth((char) ch)) {
return (char) (ch - 65248);
}
return (char) ch;
}).filter(CharacterUtils::isHalfWidth)
.mapToObj(chInt -> "" + (char) chInt).collect(Collectors.joining());
}
private static boolean isHalfWidth(int ch) {
return ch >= 32 && ch <= 126;
}
private static boolean isFullWidth(int ch) {
if (ch == '\u3000') return true;
int i = ch - 65248;
if (i < 32) return false;
return isHalfWidth((char) i);
}
}

@ -0,0 +1,88 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
/**
* Create by yixian at 2018-08-30 21:19
*/
public class ContactInfo {
private String contactName;
private int phoneAreaCode;
private String phoneNumber;
private String email;
public ContactInfo(String contactName, String phoneNumber, String email) {
this.contactName = contactName;
if (isPhoneNumber(phoneNumber)) {
phoneNumber = phoneNumber.replace(" ", "");
String auPhoneNumber = formatAustralianPhoneNumber(phoneNumber);
if (auPhoneNumber != null) {
String subPhone = auPhoneNumber.substring(3);
if (!subPhone.startsWith("1300") && !subPhone.startsWith("1800")) {
this.phoneAreaCode = Integer.parseInt(subPhone.substring(0, 1));
if (4 == phoneAreaCode) {
this.phoneNumber = subPhone.substring(1, 3) + " " + subPhone.substring(3, 6) + " " + subPhone.substring(6);
} else {
this.phoneNumber = subPhone.substring(1, 5) + " " + subPhone.substring(5);
}
}
} else {
this.phoneNumber = formatOtherPhoneNumber(phoneNumber);
}
}
EmailValidator validator = new EmailValidator();
this.email = validator.isValid(email, null) ? email : "";
}
private boolean isPhoneNumber(String phoneNumber) {
return phoneNumber.matches("^\\+?\\d+$");
}
private String formatAustralianPhoneNumber(String phoneNumber) {
if (phoneNumber.startsWith("+61") && phoneNumber.length() == 12) {
return phoneNumber;
}
if (phoneNumber.startsWith("61") && phoneNumber.length() == 11) {
return "+" + phoneNumber;
}
if (phoneNumber.startsWith("0") && phoneNumber.length() == 10) {
return "+61" + phoneNumber.substring(1);
}
if (phoneNumber.length() == 9) {
return "+61" + phoneNumber;
}
return null;
}
private String formatOtherPhoneNumber(String phoneNumber) {
String num = phoneNumber.replace(" ", "");
if (num.startsWith("+")) {
return num.replace("+", "");
}
if (num.startsWith("00")) {
return num.substring(2);
}
if (num.startsWith("1") && num.length() == 11) {
//中国手机号
return "86" + num;
}
return null;
}
public String getContactName() {
return contactName;
}
public int getPhoneAreaCode() {
return phoneAreaCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getEmail() {
return email;
}
}

@ -0,0 +1,107 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
import org.apache.commons.lang3.time.DateUtils;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Create by yixian at 2018-08-30 21:13
*/
public class ReportingPartyData implements ATOBulkLine {
private final String identifier = "RPDR";
private Date periodStart;
private Date periodEnd;
private String abn;
private String branchNumber;
private String registerName;
private String tradingName;
private AddressInfo address;
private AddressInfo postalAddress;
private ContactInfo contactInfo;
private String softwareProductType;
private Map<Integer, BusinessData> businesses = new HashMap<>();
public ReportingPartyData(Date periodStart, Date periodEnd, String abn, String branchNumber, String registerName, String softwareProductType) {
this.periodStart = DateUtils.truncate(periodStart, Calendar.DATE);
this.periodEnd = DateUtils.truncate(periodEnd, Calendar.DATE);
this.abn = abn;
this.branchNumber = branchNumber;
this.registerName = registerName;
this.softwareProductType = softwareProductType;
}
public ReportingPartyData setTradingName(String tradingName) {
this.tradingName = tradingName;
return this;
}
public ReportingPartyData setAddress(AddressInfo address) {
this.address = address;
return this;
}
public ReportingPartyData setPostalAddress(AddressInfo postalAddress) {
this.postalAddress = postalAddress;
return this;
}
public ReportingPartyData setContactInfo(ContactInfo contactInfo) {
this.contactInfo = contactInfo;
return this;
}
@Override
public StringBuilder buildLine(StringBuilder builder) {
StringBuilder line = new StringBuilder();
appendNParam(line, 1400, 4);
appendAParam(line, identifier, 4);
appendDParam(line, periodStart, 8);
appendDParam(line, periodEnd, 8);
appendNParam(line, abn, 11);
appendNParam(line, branchNumber, 3);
appendAParam(line, registerName, 200);
appendAParam(line, tradingName, 200);
appendAParam(line, address.getAddress(), 38 * 2);
appendAParam(line, address.getSuburb(), 27);
appendAParam(line, address.getState(), 3);
appendNParam(line, address.getPostCode(), 4);
appendAParam(line, address.getCountry(), 50);
appendAParam(line, postalAddress.getAddress(), 38 * 2);
appendAParam(line, postalAddress.getSuburb(), 27);
appendAParam(line, postalAddress.getState(), 3);
appendNParam(line, postalAddress.getPostCode(), 4);
appendAParam(line, postalAddress.getCountry(), 50);
appendAParam(line, contactInfo.getContactName(), 40);
appendNParam(line, contactInfo.getPhoneAreaCode(), 2);
appendAParam(line, contactInfo.getPhoneNumber(), 15);
appendAParam(line, contactInfo.getEmail(), 76);
appendAParam(line, softwareProductType, 80);
appendFiller(line, 429);
builder.append(line);
businesses.values().stream()
.filter(biz -> biz.transactionCount() > 0)
.forEach(biz -> biz.buildLine(builder));
return builder;
}
public int businessCount() {
return (int) businesses.values().stream().filter(biz -> biz.transactionCount() > 0).count();
}
public int transactionCount() {
return businesses.values().stream().mapToInt(BusinessData::transactionCount).sum();
}
public BusinessData findBusiness(Integer clientId) {
return businesses.get(clientId);
}
public void addBusiness(Integer clientId, BusinessData biz) {
businesses.put(clientId, biz);
}
}

@ -0,0 +1,46 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
/**
* Create by yixian at 2018-08-30 21:35
*/
public class TotalDataRecord implements ATOBulkLine {
private final String identifier = "FILE-TOTAL";
private int reportPartyCount;
private int businessCount;
private int transactionsCount;
public TotalDataRecord setReportPartyCount(int reportPartyCount) {
this.reportPartyCount = reportPartyCount;
return this;
}
public TotalDataRecord setBusinessCount(int businessCount) {
this.businessCount = businessCount;
return this;
}
public TotalDataRecord setTransactionsCount(int transactionsCount) {
this.transactionsCount = transactionsCount;
return this;
}
@Override
public StringBuilder buildLine(StringBuilder builder) {
StringBuilder line = new StringBuilder();
appendNParam(line, 1400, 4);
appendAParam(line, identifier, 10);
appendNParam(line, getRecords(), 10);
appendNParam(line, reportPartyCount, 10);
appendNParam(line, businessCount, 10);
appendNParam(line, transactionsCount, 10);
appendFiller(line, 1346);
builder.append(line);
return builder;
}
private int getRecords() {
return reportPartyCount + businessCount + transactionsCount + 2;
}
}

@ -0,0 +1,82 @@
package au.com.royalpay.payment.manage.analysis.beans.ato;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.DateTime;
import java.math.BigDecimal;
import java.util.Date;
/**
* Create by yixian at 2018-08-30 21:30
*/
public class TransactionSummaryData implements ATOBulkLine {
private final String identifier = "TSDR";
private String customer;
private String description;
private String accountNumber;
private String settleAccountName;
private String settleBSB;
private String settleAccountNo;
private Date periodStart;
private Date periodEnd;
private String currency = "AUD";
private BigDecimal grossPayment;
private int transactionCount;
private BigDecimal totalRefund;
private int refundCount;
private BigDecimal cashoutAmount = BigDecimal.ZERO;
private int cashoutCount = 0;
private BigDecimal cashoutComponentAmount = BigDecimal.ZERO;
public TransactionSummaryData(JSONObject analysis, DateTime startOfMon, DateTime endOfMon) {
settleAccountName = analysis.getString("account_name");
settleBSB = analysis.getString("bsb_no");
settleAccountNo = analysis.getString("account_no").replaceAll("\\D", "");
periodStart = analysis.getDate("period_start");
periodEnd = analysis.getDate("period_end");
if (DateUtils.isSameDay(periodStart, periodEnd)) {
if (DateUtils.isSameDay(periodStart, startOfMon.toDate())) {
periodEnd = DateUtils.addDays(periodEnd, 1);
} else {
periodStart = DateUtils.addDays(periodStart, -1);
}
}
grossPayment = analysis.getBigDecimal("gross_payment");
transactionCount = analysis.getIntValue("pay_count");
totalRefund = analysis.getBigDecimal("total_refund");
refundCount = analysis.getIntValue("refund_count");
}
public void fillSystemInfo(String customer, String systemDesc, String systemAccount) {
this.customer = customer;
this.description = systemDesc;
this.accountNumber = systemAccount;
}
@Override
public StringBuilder buildLine(StringBuilder builder) {
StringBuilder line = new StringBuilder();
appendNParam(line, 1400, 4);
appendAParam(line, identifier, 4);
appendAParam(line, customer, 25);
appendAParam(line, description, 40);
appendAParam(line, accountNumber, 25);
appendAParam(line, settleAccountName, 30);
appendNParam(line, settleBSB, 6);
appendNParam(line, settleAccountNo, 30);
appendDParam(line, periodStart, 8);
appendDParam(line, periodEnd, 8);
appendAParam(line, currency, 3);
appendNParam(line, grossPayment.intValue(), 13);
appendNParam(line, transactionCount, 13);
appendNParam(line, totalRefund.intValue(), 13);
appendNParam(line, refundCount, 13);
appendNParam(line, cashoutAmount.intValue(), 13);
appendNParam(line, cashoutCount, 13);
appendNParam(line, cashoutComponentAmount.intValue(), 20);
appendFiller(line, 1119);
builder.append(line);
return builder;
}
}

@ -0,0 +1,11 @@
package au.com.royalpay.payment.manage.analysis.core;
import java.util.Date;
/**
* Create by yixian at 2018-08-31 1:22
*/
public interface ATOReportService {
String exportBTTPSFile(Date from, Date to);
}

@ -0,0 +1,132 @@
package au.com.royalpay.payment.manage.analysis.core.impls;
import au.com.royalpay.payment.manage.analysis.beans.ato.*;
import au.com.royalpay.payment.manage.analysis.core.ATOReportService;
import au.com.royalpay.payment.manage.mappers.payment.TransactionMapper;
import au.com.royalpay.payment.manage.mappers.system.ClientMapper;
import au.com.royalpay.payment.tools.exceptions.ServerErrorException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Create by yixian at 2018-08-31 1:23
*/
@Service
public class ATOReportServiceImpl implements ATOReportService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private TransactionMapper transactionMapper;
@Resource
private ClientMapper clientMapper;
@Value("classpath:data/category/billbuddyindustry.json")
private org.springframework.core.io.Resource industryResource;
private Map<String, String> industryMap;
@PostConstruct
public void loadIndustryConfiguration() throws IOException {
industryMap = new HashMap<>();
JSONArray config = JSON.parseArray(IOUtils.toString(industryResource.getInputStream(), Charsets.UTF_8));
for (int i = 0; i < config.size(); i++) {
JSONObject industry = config.getJSONObject(i);
loadIndustry(industry, null);
}
}
private void loadIndustry(JSONObject industry, String parentCategory) {
JSONArray children = industry.getJSONArray("children");
String category = industry.getString("category");
String code = industry.getString("mccCode");
if (category == null && parentCategory == null) {
throw new ServerErrorException("Industry Have No Category Code:" + code);
}
category = category == null ? parentCategory : category;
industryMap.put(code, category);
if (children != null && !children.isEmpty()) {
for (int i = 0; i < children.size(); i++) {
JSONObject subIndustry = children.getJSONObject(i);
loadIndustry(subIndustry, category);
}
}
}
@Override
public String exportBTTPSFile(Date from, Date to) {
String reference = "RPAY" + DateFormatUtils.format(new Date(), "yyyyMMddHHmm");
String abn = "16601619685";
String registerCompanyName = "Tunnel Show Pty Ltd";
AddressInfo companyAddr = new AddressInfo("Level 11, 15 William St", "Melbourne", "VIC", "3000", "AUS");
ContactInfo companyContact = new ContactInfo("Locky", "+61394488865", "info@royalpay.com.au");
ATOBulkData.IntermediaryData intermediary = new ATOBulkData.IntermediaryData(abn, registerCompanyName, reference)
.setAddress(companyAddr)
.setContactInfo(companyContact);
ATOBulkData data = new ATOBulkData(intermediary);
ReportingPartyData reportingParty = new ReportingPartyData(from, to, abn, "001", registerCompanyName, "INHOUSE Tunnel Show")
.setAddress(companyAddr)
.setPostalAddress(companyAddr)
.setContactInfo(companyContact)
.setTradingName("RoyalPay");
intermediary.addReportingParty(reportingParty);
DateTime fromDateTime = new DateTime(from).withTimeAtStartOfDay();
DateTime toDateTime = new DateTime(to).plusDays(1).withTimeAtStartOfDay();
DateTime startOfMon = new DateTime(fromDateTime);
DateTime endOfMon = new DateTime(fromDateTime.plusMonths(1).withDayOfMonth(1));
while (endOfMon.isBefore(toDateTime)) {
logger.debug("Exporting date range:" + startOfMon.toString("yyyy-MM-dd") + " ~ " + endOfMon.toString("yyyy-MM-dd"));
loadMonthTransactions(reportingParty, startOfMon, endOfMon);
startOfMon = new DateTime(endOfMon);
endOfMon = new DateTime(endOfMon.plusMonths(1));
}
endOfMon = new DateTime(toDateTime);
logger.debug("Exporting date range:" + startOfMon.toString("yyyy-MM-dd") + " ~ " + endOfMon.toString("yyyy-MM-dd"));
loadMonthTransactions(reportingParty, startOfMon, endOfMon);
logger.info("output BTTPS file");
return data.outputBTTPS();
}
private void loadMonthTransactions(ReportingPartyData reportingParty, DateTime startOfMon, DateTime endOfMon) {
List<Integer> clients = clientMapper.listClientsWithTransactionsSettled(startOfMon.toDate(), endOfMon.toDate());
clients.parallelStream().forEach(clientId -> loadClientMonthTransactions(reportingParty, clientId, startOfMon, endOfMon));
}
private void loadClientMonthTransactions(ReportingPartyData reportingParty, Integer clientId, DateTime startOfMon, DateTime endOfMon) {
BusinessData biz = reportingParty.findBusiness(clientId);
logger.debug("Exporting date range for client[" + clientId + "]:" + startOfMon.toString("yyyy-MM-dd") + " ~ " + endOfMon.toString("yyyy-MM-dd"));
if (biz == null) {
JSONObject cli = clientMapper.findClientIgnoreInvalid(clientId);
AddressInfo addr = new AddressInfo(cli.getString("address"), cli.getString("suburb"), cli.getString("state"), cli.getString("postcode"), cli.getString("country"));
ContactInfo contact = new ContactInfo(cli.getString("contact_person"), cli.getString("contact_phone"), cli.getString("contact_email"));
String industry = industryMap.getOrDefault(cli.getString("royalpayindustry"), cli.getString("royalpayindustry"));
String clientMoniker = cli.getString("client_moniker");
biz = new BusinessData(cli.getDate("create_time"), cli.getString("abn"), cli.getString("acn"),
cli.getString("company_name"), cli.getString("short_name"), clientMoniker,
industry, "O")
.setAddress(addr)
.setPostalAddress(addr)
.setContactInfo(contact);
reportingParty.addBusiness(clientId, biz);
}
List<JSONObject> analysisList = transactionMapper.analysisForATOReport(clientId, startOfMon.toDate(), endOfMon.toDate());
for (JSONObject analysis : analysisList) {
biz.addTransaction(new TransactionSummaryData(analysis, startOfMon, endOfMon));
}
}
}

@ -34,6 +34,7 @@ import au.com.royalpay.payment.tools.utils.XmlFormatUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.dom4j.Element;
import org.slf4j.Logger;
@ -245,7 +246,8 @@ public class TestController implements ApplicationEventPublisherAware {
List<JSONObject> orders = orderMapper.listOrdersWithNoTransactions();
for (JSONObject order : orders) {
try {
paymentApi.checkOrderStatus(order.getString("order_id"), order.getString("channel"), true);
String orderChannel = StringUtils.defaultIfEmpty(order.getString("order_channel"), order.getString("channel"));
paymentApi.checkOrderStatus(order.getString("order_id"), orderChannel, true);
} catch (Exception e) {
logger.error("fix transaction error:" + order.getString("order_id"));
}
@ -262,7 +264,7 @@ public class TestController implements ApplicationEventPublisherAware {
if (order == null) {
throw new OrderNotExistsException();
}
String channel = order.getString("channel");
String channel = StringUtils.defaultIfEmpty(order.getString("order_channel"), order.getString("channel"));
JSONObject res = new JSONObject();
switch (channel) {
case "Wechat":

@ -8,6 +8,7 @@ import com.alibaba.fastjson.JSONObject;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import com.github.miemiedev.mybatis.paginator.domain.PageList;
import org.apache.ibatis.annotations.Param;
import org.joda.time.DateTime;
import java.math.BigDecimal;
import java.util.Date;
@ -142,4 +143,6 @@ public interface TransactionMapper {
void removeSettleRemark(@Param("clearing_id") int clearingId);
List<JSONObject> getHfClearAmount(JSONObject params);
List<JSONObject> analysisForATOReport(@Param("clientId") int clientId, @Param("from") Date startOfMon, @Param("to") Date endOfMon);
}

@ -83,6 +83,8 @@ public interface ClientMapper {
List<JSONObject> listBySubMerchantId(@Param("sub_merchant_id") String sub_merchant_id);
List<Integer> listClientsWithTransactionsSettled(@Param("from") Date fromDate, @Param("to") Date toDate);
@AutoSql(type = SqlType.SELECT)
List<JSONObject> AllClients();

@ -101,13 +101,17 @@
<select id="analysisTransFlow" resultType="com.alibaba.fastjson.JSONObject">
SELECT
count(DISTINCT o.order_id) order_count,
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and o.currency!='CNY',o.display_amount,0)),0)
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and
o.currency!='CNY',o.display_amount,0)),0)
display_amount,
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and o.currency!='CNY' and
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and o.currency!='CNY'
and
o.pre_authorization=1,o.display_amount,0)),0) pre_display_amount,
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and o.currency='CNY',o.display_amount,0)),0)
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and
o.currency='CNY',o.display_amount,0)),0)
cny_display_amount,
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and o.currency='CNY' and
ifnull(sum(if(t.transaction_type='Credit' and t.refund_id is null and t.system_generate=0 and o.currency='CNY'
and
o.pre_authorization=1,o.display_amount,0)),0) pre_cny_display_amount,
ifnull(sum(if(t.transaction_type='Credit',t.clearing_amount,0)),0) paid_fee,
sum(if(t.transaction_type='Debit' AND t.refund_id is NOT NULL,t.clearing_amount,0))*-1
@ -192,7 +196,8 @@
</select>
<select id="getClientsUnClearedAmount" resultType="com.alibaba.fastjson.JSONObject">
SELECT ifnull(sum(if(transaction_type = 'Credit', clearing_amount, -clearing_amount)), 0) clearing_amount,client_id
SELECT ifnull(sum(if(transaction_type = 'Credit', clearing_amount, -clearing_amount)), 0)
clearing_amount,client_id
FROM pmt_transactions
where clearing_status = 0 AND
client_id in
@ -244,6 +249,8 @@
</select>
<select id="listUnclearedTransactionsOfClient" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT
c.client_id,
c.client_moniker,
@ -275,6 +282,8 @@
)
GROUP BY t.transaction_id
ORDER BY t.transaction_type, t.transaction_time
]]>
</select>
@ -420,6 +429,8 @@
<select id="getMerchantAmount" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT temp.*
FROM (
SELECT
@ -440,11 +451,15 @@
t.transaction_time >= #{from} AND t.transaction_time < #{to} AND t.channel != 'Settlement'
GROUP BY t.client_id) temp
WHERE clearing_amount != 0
]]>
</select>
<select id="getSumMerchantAmount" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT SUM(temp.clearing_amount) clearing_amount
FROM
(SELECT round(sum(if(t.transaction_type = 'Credit', t.clearing_amount, -t.clearing_amount)) *
@ -459,14 +474,20 @@
t.transaction_time >= #{from} AND t.transaction_time < #{to} AND t.channel != 'Settlement'
GROUP BY t.client_id) temp
WHERE temp.clearing_amount != 0
]]>
</select>
<select id="analysisTotalTransactions" resultType="java.lang.Double">
<![CDATA[
SELECT ifnull(sum(clearing_amount), 0) total
FROM pmt_transactions
WHERE client_id = #{client_id} AND date(transaction_time) >= date(#{begin}) AND
date(transaction_time) <= date(#{end})
]]>
</select>
<select id="listTransactionsOfClearingTask" resultType="com.alibaba.fastjson.JSONObject">
@ -507,6 +528,8 @@
<select id="TotalAmountForBDPrize" resultType="java.math.BigDecimal">
<![CDATA[
select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,-temp.clearing_amount*d.proportion))
total FROM
(SELECT l.client_id,l.clearing_amount,l.refund_id,l.transaction_type,o.create_time FROM pmt_transactions l
@ -519,6 +542,8 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
d.is_valid = '1'
AND (d.end_date is null or d.end_date > temp.create_time)
and d.bd_id=#{bd_id}
]]>
</select>
@ -554,6 +579,8 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
</select>
<select id="listTransactionsForCityPartnerCommission" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT
sum(if(t.transaction_type = 'Credit', t.clearing_amount, -t.clearing_amount)) * b.proportion
total,
@ -599,6 +626,8 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
WHERE year(t.transaction_time) = #{year} AND month(t.transaction_time) = #{month} AND t.channel != 'Settlement'
GROUP BY t.client_id, trade_date, channel
ORDER BY c.org_id ASC, t.client_id ASC, trade_date ASC
]]>
</select>
@ -623,6 +652,8 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
<select id="listTransactionsForAgentCommission" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT
sum(if(t.transaction_type = 'Credit', t.clearing_amount, -t.clearing_amount))
total,
@ -664,12 +695,16 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
WHERE year(t.transaction_time) = #{year} AND month(t.transaction_time) = #{month} AND t.channel != 'Settlement'
GROUP BY t.client_id, trade_date, channel
ORDER BY c.org_id ASC, t.client_id ASC, trade_date ASC
]]>
</select>
<select id="checkBalance" resultType="java.math.BigDecimal">
<![CDATA[
SELECT sum(subval)
FROM (SELECT
c.client_moniker,
@ -715,6 +750,8 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
abs(total_credit - total_debit) > 1
ORDER BY
subval DESC) t
]]>
</select>
<select id="listDailyTransactions" resultType="com.alibaba.fastjson.JSONObject">
@ -768,6 +805,8 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
</select>
<select id="listTransactionsOfClearingOrder" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT
t.*,
o.display_amount,
@ -780,6 +819,8 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
LEFT JOIN pmt_orders o ON o.order_id = t.order_id
LEFT JOIN pmt_refunds r ON r.refund_id = t.refund_id
WHERE t.channel != 'Settlement' AND t.clearing_order = #{clearing_order}
]]>
</select>
<select id="listSettlementTransactions" resultType="com.alibaba.fastjson.JSONObject">
@ -867,5 +908,22 @@ select sum(if(temp.transaction_type='Credit',temp.clearing_amount*d.proportion,
AND transaction_time &lt;= #{dateto}
GROUP BY transaction_type
</select>
<select id="analysisForATOReport" resultType="com.alibaba.fastjson.JSONObject">
select sum(if(transaction_type='Credit',t.clearing_amount,0)) gross_payment,
sum(if(transaction_type='Credit',1,0)) pay_count,
sum(if(transaction_type='Debit',t.clearing_amount,0)) total_refund,
sum(if(transaction_type='Debit',1,0)) refund_count,
min(t.transaction_time) period_start,
max(t.transaction_time) period_end,
cd.bsb_no,
cd.account_no,
cd.account_name
from pmt_transactions t
inner join log_clearing_detail cd on cd.clear_detail_id=t.clearing_order
where t.client_id=#{clientId} and t.system_generate=0 and t.clearing_status=1 and t.transaction_time between #{from} and #{to}
and t.channel!='Settlement'
group by cd.bsb_no,cd.account_no
order by period_start
</select>
</mapper>

@ -272,14 +272,12 @@
<select id="listUnAuthPartners" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT
client_id,
client_moniker,
approve_time
FROM sys_clients
WHERE is_valid = 1 AND approve_result = 2 AND source = 4 AND datediff(now(), approve_time) <= #{auth_days}
]]>
</select>
@ -358,6 +356,11 @@
and contact_email = #{contact_email}
</if>
</select>
<select id="listClientsWithTransactionsSettled" resultType="int">
select distinct c.client_id from sys_clients c
inner join pmt_transactions t on t.client_id=c.client_id and t.clearing_status=1 and t.system_generate=0
where t.transaction_time between #{from} and #{to}
</select>
<select id="findByhfPayUrlNotNull" resultType="com.alibaba.fastjson.JSONObject">
select client_id,client_moniker FROM sys_clients WHERE hf_pay_url != ''

@ -0,0 +1,331 @@
[
{
"children": [
{
"children": [],
"label": "机票",
"mccCode": "10001"
},
{
"children": [],
"label": "旅游行业",
"mccCode": "10002"
},
{
"children": [],
"label": "私人定制旅游",
"mccCode": "10003"
},
{
"children": [],
"label": "租车",
"mccCode": "10004",
"category": "RENTAL"
},
{
"children": [],
"label": "巴士",
"mccCode": "10005"
}
],
"label": "旅游出行",
"mccCode": "1",
"category": "PROFRSSIONAL SERV"
},
{
"children": [
{
"children": [],
"label": "饭店",
"mccCode": "20001"
},
{
"children": [],
"label": "奶茶店",
"mccCode": "20002"
},
{
"children": [],
"label": "烧烤",
"mccCode": "20003"
},
{
"children": [],
"label": "火锅",
"mccCode": "20004"
},
{
"children": [],
"label": "Coffee",
"mccCode": "20005"
},
{
"children": [],
"label": "酒吧",
"mccCode": "20006"
}
],
"label": "餐饮",
"mccCode": "2",
"category": "RETAIL"
},
{
"children": [
{
"children": [],
"label": "培训类",
"mccCode": "30001"
},
{
"children": [],
"label": "移民留学",
"mccCode": "30002"
},
{
"children": [],
"label": "私人幼儿园",
"mccCode": "30003"
}
],
"label": "教育",
"mccCode": "3",
"category": "SCHOOLS & EDUCATION SERV"
},
{
"children": [
{
"children": [],
"label": "换汇",
"mccCode": "40001"
},
{
"children": [],
"label": "房产",
"mccCode": "40002"
}
],
"label": "商务咨询",
"mccCode": "4",
"category": "FINANCE"
},
{
"children": [
{
"children": [],
"label": "公众号服务商",
"mccCode": "50001"
},
{
"children": [],
"label": "各种媒体类宣传",
"mccCode": "50002"
}
],
"label": "传媒",
"mccCode": "5",
"category": "PROFESSIONAL SERV"
},
{
"children": [
{
"children": [],
"label": "美容院",
"mccCode": "60001"
},
{
"children": [],
"label": "医疗美容",
"mccCode": "60002"
}
],
"label": "医美",
"mccCode": "6",
"category": "FITNESS"
},
{
"children": [
{
"label": "超市",
"mccCode": "70001"
},
{
"children": [],
"label": "服装店",
"mccCode": "70002"
},
{
"children": [],
"label": "鞋店",
"mccCode": "70003"
},
{
"children": [],
"label": "珠宝店",
"mccCode": "70004"
},
{
"children": [],
"label": "箱包",
"mccCode": "70005"
}
],
"label": "零售",
"mccCode": "7",
"category": "RETAIL"
},
{
"children": [
{
"children": [],
"label": "桌游吧",
"mccCode": "80001"
},
{
"children": [],
"label": "演唱会",
"mccCode": "80002",
"category": "UTILITIES"
},
{
"children": [],
"label": "马术训练",
"mccCode": "80003",
"category": "FITNESS"
},
{
"children": [],
"label": "瑜伽",
"mccCode": "80004",
"category": "FITNESS"
},
{
"children": [],
"label": "健身",
"mccCode": "80005",
"category": "FITNESS"
},
{
"children": [],
"label": "社团",
"mccCode": "80006"
},
{
"children": [],
"label": "网吧",
"mccCode": "80007",
"category": "EQUIPMENT HIRE"
},
{
"children": [],
"label": "KTV",
"mccCode": "80008",
"category": "EQUIPMENT HIRE"
},
{
"children": [],
"label": "电影",
"mccCode": "80009"
}
],
"label": "休闲娱乐",
"mccCode": "8",
"category": "OTHER"
},
{
"children": [
{
"children": [],
"label": "摄影",
"mccCode": "90001",
"category": "PROFESSIONAL SERV"
},
{
"children": [],
"label": "massage",
"mccCode": "90002"
},
{
"children": [],
"label": "通讯运营商",
"mccCode": "90003",
"category": "TELECOM"
},
{
"children": [],
"label": "车行",
"mccCode": "90004",
"category": "TRADES & VOC"
},
{
"children:": [],
"label": "软件服务",
"mccCode": "90005",
"category": "PROFESSIONAL SERV"
}
],
"label": "其他服务类",
"mccCode": "9",
"category": "OTHER"
},
{
"children": [
{
"children": [],
"label": "Hotel",
"mccCode": "100001"
},
{
"children": [],
"label": "Motel",
"mccCode": "100002"
}
],
"label": "酒店",
"mccCode": "10",
"category": "RENTAL"
},
{
"children": [
{
"children": [],
"label": "代购",
"mccCode": "110001"
},
{
"children": [],
"label": "物流(大宗出口贸易)",
"mccCode": "110002"
},
{
"children": [],
"label": "红酒出口",
"mccCode": "110003"
},
{
"children": [],
"label": "综合电商",
"mccCode": "110004"
}
],
"label": "出口贸易",
"mccCode": "11",
"category": "TRADES & VOC"
},
{
"children": [
{
"children": [],
"label": "建材",
"mccCode": "120001"
},
{
"children": [],
"label": "家居",
"mccCode": "120002"
}
],
"label": "家居建材",
"mccCode": "12",
"category": "TRADES & VOC"
}
]

@ -0,0 +1,34 @@
package au.com.royalpay.payment.manage.process.ato;
import au.com.royalpay.payment.manage.analysis.core.ATOReportService;
import org.apache.commons.io.FileUtils;
import org.joda.time.DateTime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* Create by yixian at 2018-08-31 14:40
*/
@SpringBootTest
@ActiveProfiles({"proxy", "wechat"})
@TestPropertySource(properties = {"spring.datasource.username=root", "spring.datasource.password=ZOIBhellor0yalpay"})
@RunWith(SpringRunner.class)
public class ExportATOReport {
@Resource
private ATOReportService atoReportService;
@Test
public void export() throws IOException {
String content = atoReportService.exportBTTPSFile(DateTime.parse("2017-07-01").toDate(), DateTime.parse("2017-07-31").toDate());
FileUtils.write(new File("C:\\Users\\yixian\\Documents\\royalpay2017070120170731.bttps"), content, StandardCharsets.US_ASCII);
}
}

@ -0,0 +1,19 @@
package au.com.royalpay.payment.manage.valid;
import au.com.royalpay.payment.manage.analysis.beans.ato.CharacterUtils;
import com.github.stuxuhai.jpinyin.PinyinException;
import com.github.stuxuhai.jpinyin.PinyinFormat;
import com.github.stuxuhai.jpinyin.PinyinHelper;
import org.junit.Test;
/**
* Create by yixian at 2018-09-06 8:57
*/
public class JPinYinTest {
@Test
public void testWithOtherCharacters() throws PinyinException {
String test = "你好1234512hello guys";
System.out.println(CharacterUtils.convertFullWidthToHalfWidth(CharacterUtils.convertToPinYin(test)));
}
}

@ -0,0 +1,20 @@
package au.com.royalpay.payment.manage.valid;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.junit.Test;
import java.util.Date;
/**
* Create by yixian at 2018-08-31 15:27
*/
public class TestJodaFormat {
@Test
public void testFormat(){
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSZZ");
String dateStr = formatter.print(new DateTime(new Date()));
System.out.println(dateStr);
}
}
Loading…
Cancel
Save