diff --git a/src/document/openapi/cn/document.yml b/src/document/openapi/cn/document.yml index 990cf3488..4bfbc5465 100644 --- a/src/document/openapi/cn/document.yml +++ b/src/document/openapi/cn/document.yml @@ -865,13 +865,13 @@ paths: code_url: type: string description: 付款码字符串,商户可自行生成二维码 # todo - /alipay/partners/{partner_code}/orders/{partner_order_id}: + /web_gateway/partners/{partner_code}/orders/{partner_order_id}: put: summary: 渠道网关订单下单 x-sort-order: 0 description: | - 创建订单后跳转到返回的pay_url(需附加签名参数和redirect参数),随后进入支付宝支付页面完成支付 - 该接口现仅支持支付宝。 + 创建订单后跳转到返回的pay_url(需附加签名参数和redirect参数),随后进入渠道网关支付页面完成支付 + 该接口现仅支持支付宝和Alipay+。 tags: - ChannelGateway parameters: diff --git a/src/document/openapi/en/components_order.yml b/src/document/openapi/en/components_order.yml index 682cd86f2..e9f7a5617 100644 --- a/src/document/openapi/en/components_order.yml +++ b/src/document/openapi/en/components_order.yml @@ -51,6 +51,7 @@ orderWithChannel: enum: - Alipay - Wechat + - AlipayPlus type: object required: - channel diff --git a/src/document/openapi/en/document.yml b/src/document/openapi/en/document.yml index 831f9cd80..0665f2fa0 100644 --- a/src/document/openapi/en/document.yml +++ b/src/document/openapi/en/document.yml @@ -145,10 +145,10 @@ tags: Retail payment API for merchants has their own machine. Has 2 modes: - Merchant scanner scan customer provided payment code.(B scan C) - Customer scan merchant provided collection code. (C scan B) - - name: AlipayOnline + - name: ChannelGateway description: | - Use for Alipay Payment in PC Website. After create order, jump to the pay_url returned and attach sign params and redirect param. Then enter Alipay page to finish payment. - Alipay Channel only + Use for Channel Cashier Payment in PC Website. After create order, jump to the pay_url returned and attach sign params and redirect param. Then enter Channel cashier page to finish payment. + Alipay and AlipayPlus Channels only - name: CB Bank description: | @@ -260,7 +260,7 @@ paths: - SDK Payment - CB Bank - RetailPay - - AlipayOnline + - ChannelGateway - CardPayment - MiniProgram - MobileH5 @@ -288,7 +288,7 @@ paths: - SDK Payment - CB Bank - RetailPay - - AlipayOnline + - ChannelGateway - CardPayment - MiniProgram - MobileH5 @@ -888,14 +888,14 @@ paths: code_url: type: string description: QR Code string. Partners can create the payment QR Code according to this value. - /alipay/partners/{partner_code}/orders/{partner_order_id}: + /web_gateway/partners/{partner_code}/orders/{partner_order_id}: put: - summary: Alipay WEB Order + summary: Channel WEB Order description: | - Use for Alipay Payment in PC Website. After create order, jump to the pay_url returned and attach sign params and redirect param. Then enter Alipay page to finish payment. - Alipay Channel Only. + Use for Channel Payment Cashier in PC Website. After create order, jump to the pay_url returned and attach sign params and redirect param. Then enter Channel cashier page to finish payment. + Alipay and AlipayPlus only at the moment. tags: - - AlipayOnline + - ChannelGateway parameters: - name: partner_code in: path @@ -1106,7 +1106,7 @@ paths: - MobileH5 - MiniProgram - CardPayment - - AlipayOnline + - ChannelGateway - RetailPay - CB Bank - SDK Payment @@ -1151,7 +1151,7 @@ paths: - MobileH5 - MiniProgram - CardPayment - - AlipayOnline + - ChannelGateway - RetailPay - CB Bank - SDK Payment @@ -1331,7 +1331,7 @@ paths: - SDK Payment - CB Bank - RetailPay - - AlipayOnline + - ChannelGateway - CardPayment - MiniProgram - MobileH5 @@ -1376,7 +1376,7 @@ paths: description: Currency, AUD channel: type: string - description: Payment Channel Alipay, AlipayOnline, Wechat + description: Payment Channel Alipay, AlipayOnline, Wechat, AlipayPlus create_time: type: string format: 'date-time' diff --git a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java index 315f36874..08751c31a 100644 --- a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java +++ b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java @@ -56,6 +56,8 @@ public interface CleanService { void settlementAba(Date date, HttpServletResponse response) throws IOException; + List getCustomizedSettleDaily(Date date); + void getCustomizedSettleABA(int logId, HttpServletResponse response); void getSettlementFilesForBatch(String batchId, HttpServletResponse resp) throws IOException; diff --git a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java index e61bfb6ca..a2feb922b 100644 --- a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java @@ -668,6 +668,15 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider } } + @Override + public List getCustomizedSettleDaily(Date date) { + DateTime dtFrom = new DateTime(date); + DateTime dtTo = dtFrom.plusDays(1); + List logs = customizedSettleLogMapper.listByDate(dtFrom.toDate(), dtTo.toDate()); + logs.forEach(log -> log.put("aba_file", String.format("/sys/settlement/customized_settle/%s/aba_file", log.getIntValue("log_id")))); + return logs; + } + @Override public void getCustomizedSettleABA(int logId, HttpServletResponse response) { JSONObject log = customizedSettleLogMapper.find(logId); @@ -686,7 +695,8 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider settle.put("client_moniker", log.getString("client_moniker")); ABAFile file = generateSettleAbaFile(bank, group, new Date(), Collections.singletonList(settle)); response.setContentType("application/octet-stream;"); - response.addHeader("Content-Disposition", "attachment; filename=" + file.filename()); + String filename = String.format("[%s]Manual_Settle_%s_%s.aba", logId, log.getString("client_moniker"), DateTime.now().toString("yyyyMMdd")); + response.addHeader("Content-Disposition", "attachment; filename=" + filename); try (OutputStream ous = response.getOutputStream()) { ous.write(file.output(1)); ous.flush(); diff --git a/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java b/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java index f2ca28d44..7e0d4a5f4 100644 --- a/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java +++ b/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java @@ -9,6 +9,7 @@ import au.com.royalpay.payment.tools.exceptions.BadRequestException; import au.com.royalpay.payment.tools.merchants.beans.BalanceGroup; import au.com.royalpay.payment.tools.permission.enums.ManagerRole; import com.alibaba.fastjson.JSONObject; +import org.joda.time.DateTime; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -170,6 +171,12 @@ public class SettlementDevController { } } + @GetMapping("/customized_settle/dates/{date}") + public List customizedSettleInDay(@PathVariable String date){ + Date dt = DateTime.parse(date).withTimeAtStartOfDay().toDate(); + return cleanService.getCustomizedSettleDaily(dt); + } + @GetMapping("/customized_settle/{logId}/aba_file") public void getCustomizedSettleABAFile(@PathVariable int logId,HttpServletResponse response){ cleanService.getCustomizedSettleABA(logId, response); diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/log/CustomizedSettleLogMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/log/CustomizedSettleLogMapper.java index 0d34da48e..46c7587c6 100644 --- a/src/main/java/au/com/royalpay/payment/manage/mappers/log/CustomizedSettleLogMapper.java +++ b/src/main/java/au/com/royalpay/payment/manage/mappers/log/CustomizedSettleLogMapper.java @@ -5,10 +5,17 @@ import com.yixsoft.support.mybatis.autosql.annotations.AutoMapper; import com.yixsoft.support.mybatis.autosql.annotations.AutoSql; import com.yixsoft.support.mybatis.autosql.annotations.SqlType; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; +import java.util.Date; +import java.util.List; + @AutoMapper(tablename = "log_settle_customized", pkName = "log_id", keyGenerator = Jdbc3KeyGenerator.class) public interface CustomizedSettleLogMapper { @AutoSql(SqlType.SELECT) JSONObject find(@Param("log_id") int logId); + + @Select("select * from log_settle_customized where create_time between #{date_from} and #{date_to}") + List listByDate(@Param("date_from") Date dateFrom,@Param("date_to") Date dateTo); } diff --git a/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java b/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java index b905f8284..77a5fe602 100644 --- a/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java @@ -2705,16 +2705,27 @@ public class ClientManagerImpl implements ClientManager, ManagerTodoNoticeProvid public void writeAggregatePoster(JSONObject manager, String clientMoniker, OutputStream ous) { JSONObject client = clientDetail(manager, clientMoniker); try { - logger.debug("downloading aggregate poster from {}", clientMoniker); + String url = PlatformEnvironment.getEnv().concatUrl("/static/images/new_aggregate_poster.png"); + logger.debug("downloading aggregate poster from {}--{}", clientMoniker, url); HttpRequestResult boardBackgroundResult = new HttpRequestGenerator( - PlatformEnvironment.getEnv().concatUrl("/static/images/new_aggregate_poster.png"), RequestMethod.GET).execute(); + url, RequestMethod.GET).execute(); if (boardBackgroundResult.isSuccess()) { - InputStream ins = boardBackgroundResult.getResponseContentStream(); - BufferedImage background = ImageIO.read(ins); - IOUtils.closeQuietly(ins); - ImageIO.write(background, "jpeg", ous); - ous.flush(); - IOUtils.closeQuietly(ous); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + InputStream ins = boardBackgroundResult.getResponseContentStream(); + BufferedImage background = ImageIO.read(ins); + IOUtils.closeQuietly(ins); + logger.debug("aggregate image size {}x{}", background.getWidth(), background.getHeight()); + BufferedImage img = new BufferedImage(background.getWidth(), background.getHeight(), BufferedImage.TYPE_3BYTE_BGR); + Graphics g = img.getGraphics(); + g.drawImage(background, 0, 0, null); + g.dispose(); + ImageIO.write(img, "jpeg", bos); + bos.flush(); + byte[] imageBytes = bos.toByteArray(); + logger.debug("aggregate image length {}", imageBytes.length); + ous.write(imageBytes); + IOUtils.closeQuietly(ous); + } } else { logger.error("get aggregate poster file failed:[{}]-{}", boardBackgroundResult.getStatusCode(), boardBackgroundResult.getResponseContentString(), boardBackgroundResult.getException()); diff --git a/src/main/java/au/com/royalpay/payment/manage/shopify/auth/domain/service/ShopifyRequestValidator.java b/src/main/java/au/com/royalpay/payment/manage/shopify/auth/domain/service/ShopifyRequestValidator.java index cf90d7473..3c7daab6f 100644 --- a/src/main/java/au/com/royalpay/payment/manage/shopify/auth/domain/service/ShopifyRequestValidator.java +++ b/src/main/java/au/com/royalpay/payment/manage/shopify/auth/domain/service/ShopifyRequestValidator.java @@ -12,23 +12,20 @@ public class ShopifyRequestValidator { private String clientSecret; public Boolean valid(ShopifyCommonParameter parameter) { - StringBuilder message =new StringBuilder(); + StringBuilder message = new StringBuilder(); message.append("code=").append(parameter.getCode()) .append("&host=").append(parameter.getHost()) .append("&shop=").append(parameter.getShop()) .append("&state=").append(parameter.getState()) .append("×tamp=").append(parameter.getTimestamp()); - return HmacVerificationUtil.hmacSHA256(message.toString(),clientSecret,parameter.getHmac()); + return HmacVerificationUtil.hmacSHA256(message.toString(), clientSecret, parameter.getHmac()); } - public boolean verifyPermission(String shopifyStoreHost, String hmac, String timestamp) { - StringBuilder message =new StringBuilder(); - message.append("shop=").append(shopifyStoreHost) - .append("×tamp=").append(timestamp); - return HmacVerificationUtil.hmacSHA256(message.toString(),clientSecret,hmac); + public boolean verifyPermission(String queryStrWithoutHmac, String hmac) { + return HmacVerificationUtil.hmacSHA256(queryStrWithoutHmac, clientSecret, hmac); } public boolean verify(String message, String hmac) { - return HmacVerificationUtil.hmacSHA256(message,clientSecret,hmac); + return HmacVerificationUtil.hmacSHA256(message, clientSecret, hmac); } } diff --git a/src/main/java/au/com/royalpay/payment/manage/shopify/auth/web/ShopifyAuthTemplateController.java b/src/main/java/au/com/royalpay/payment/manage/shopify/auth/web/ShopifyAuthTemplateController.java index c57b68a81..dcd5c9d70 100644 --- a/src/main/java/au/com/royalpay/payment/manage/shopify/auth/web/ShopifyAuthTemplateController.java +++ b/src/main/java/au/com/royalpay/payment/manage/shopify/auth/web/ShopifyAuthTemplateController.java @@ -10,9 +10,12 @@ import au.com.royalpay.payment.tools.env.PlatformEnvironment; import au.com.royalpay.payment.tools.exceptions.BadRequestException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; +import javax.servlet.http.HttpServletRequest; import java.util.regex.Pattern; @Controller @@ -33,18 +36,18 @@ public class ShopifyAuthTemplateController { * * @param shop * @param hmac - * @param timestamp * @return */ @GetMapping("/auth") @ShopifyEndpoint public String shopifyStorePermission(@RequestParam("shop") String shop, - @RequestParam("hmac") String hmac, - @RequestParam("timestamp") String timestamp) { + @RequestParam("hmac") String hmac, HttpServletRequest request) { if (!Pattern.matches("^[a-zA-Z0-9][a-zA-Z0-9\\-]*\\.myshopify\\.com", shop)) { throw new BadRequestException("Parameter shop is invalid."); } - if (!shopifyRequestValidator.verifyPermission(shop, hmac, timestamp)) { + String queryStr = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)) + .replaceQueryParam("hmac").build().getQuery(); + if (!shopifyRequestValidator.verifyPermission(queryStr, hmac)) { throw new ShopifyRequestVerifyException("This request parameters is invalid"); } ShopifyPermissionURL shopifyPermissionURL = shopifyMerchantAuthApplication.getShopifyPermissionUrl(shop); diff --git a/src/main/java/au/com/royalpay/payment/manage/shopify/support/HmacVerificationUtil.java b/src/main/java/au/com/royalpay/payment/manage/shopify/support/HmacVerificationUtil.java index 5ba85aac0..5c5b159aa 100644 --- a/src/main/java/au/com/royalpay/payment/manage/shopify/support/HmacVerificationUtil.java +++ b/src/main/java/au/com/royalpay/payment/manage/shopify/support/HmacVerificationUtil.java @@ -5,6 +5,8 @@ import org.apache.commons.codec.digest.HmacUtils; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.crypto.RuntimeCryptoException; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.crypto.Mac; import javax.crypto.SecretKey; @@ -15,6 +17,10 @@ import java.security.Security; import java.util.Locale; public class HmacVerificationUtil { + private static final Logger logger = LoggerFactory.getLogger(HmacVerificationUtil.class); + + private HmacVerificationUtil() { + } public static boolean checkParameters(String message, String secret, String hmac) { try { @@ -32,6 +38,7 @@ public class HmacVerificationUtil { public static boolean hmacSHA256(String input, String key, String hmac) { String encode = encode(input, key, HmacAlgorithms.HMAC_SHA_256); + logger.debug("input={}; key={}; encoded={}; request-hmac: {}", input, key, encode, hmac); return StringUtils.equals(encode, hmac); } diff --git a/src/main/java/au/com/royalpay/payment/manage/shopify/support/ShopifyRequestInfoInterceptor.java b/src/main/java/au/com/royalpay/payment/manage/shopify/support/ShopifyRequestInfoInterceptor.java index 658dbdee1..268b7619a 100644 --- a/src/main/java/au/com/royalpay/payment/manage/shopify/support/ShopifyRequestInfoInterceptor.java +++ b/src/main/java/au/com/royalpay/payment/manage/shopify/support/ShopifyRequestInfoInterceptor.java @@ -15,6 +15,9 @@ public class ShopifyRequestInfoInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!(handler instanceof HandlerMethod)){ + return super.preHandle(request, response, handler); + } Method method = ((HandlerMethod) handler).getMethod(); if(HttpMethod.GET.matches(request.getMethod())) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9d98fb3f4..31a4d01cd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -181,7 +181,7 @@ shopify: auth: apiKey: 99e631dd0faa1076ceffae36cf91a93b apiSecretKey: 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 - scope: write_orders,read_customers,write_payment_gateways,write_payment_sessions + scope: write_orders,write_payment_gateways,write_payment_sessions diff --git a/src/main/ui/static/analysis/clearing-log.js b/src/main/ui/static/analysis/clearing-log.js index de03c8a8f..fd447036c 100644 --- a/src/main/ui/static/analysis/clearing-log.js +++ b/src/main/ui/static/analysis/clearing-log.js @@ -269,6 +269,18 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func $state.reload(); }; + $scope.showCustomizedSettle = function () { + $uibModal.open({ + templateUrl: '/static/analysis/templates/dialog_customized_settle.html', + controller: 'customizedDialogCtrl', + resolve: { + customizedLogs: function () { + return $http.get('/sys/settlement/customized_settle/dates/' + $scope.datePattern) + } + } + }) + } + let nowStr = $filter('date')(new Date(), "yyyy-MM-dd"); $scope.datePattern = $stateParams.date; if ($scope.datePattern == nowStr) { @@ -375,6 +387,10 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func }) } }]); + + app.controller('customizedDialogCtrl', ['$scope', 'customizedLogs', function ($scope, customizedLogs) { + $scope.customizedLogs = customizedLogs.data + }]) app.controller('settlementTransactionsCtrl', ['$scope', '$stateParams', 'detail', function ($scope, $stateParams, detail) { $scope.ctrl = {channel: null}; $scope.report = detail.data; diff --git a/src/main/ui/static/analysis/templates/dialog_customized_settle.html b/src/main/ui/static/analysis/templates/dialog_customized_settle.html new file mode 100644 index 000000000..cc9223f51 --- /dev/null +++ b/src/main/ui/static/analysis/templates/dialog_customized_settle.html @@ -0,0 +1,27 @@ + + + \ No newline at end of file diff --git a/src/main/ui/static/analysis/templates/settlement_detail.html b/src/main/ui/static/analysis/templates/settlement_detail.html index 91aeb1787..042d4da81 100644 --- a/src/main/ui/static/analysis/templates/settlement_detail.html +++ b/src/main/ui/static/analysis/templates/settlement_detail.html @@ -40,9 +40,13 @@ ng-disabled="noticeResend"> Send Settlement Notice + + Settle Tasks diff --git a/src/test/java/au/com/royalpay/payment/manage/shopify/support/HmacVerificationUtilTest.java b/src/test/java/au/com/royalpay/payment/manage/shopify/support/HmacVerificationUtilTest.java new file mode 100644 index 000000000..acc19713a --- /dev/null +++ b/src/test/java/au/com/royalpay/payment/manage/shopify/support/HmacVerificationUtilTest.java @@ -0,0 +1,25 @@ +package au.com.royalpay.payment.manage.shopify.support; + +import org.junit.jupiter.api.Test; +import org.springframework.web.util.UriComponentsBuilder; + +import static org.junit.jupiter.api.Assertions.*; + +class HmacVerificationUtilTest { + + @Test + void checkParameters() { + String message = "host=Z2Vlay10ZXN0LXNob3AubXlzaG9waWZ5LmNvbS9hZG1pbg&shop=geek-test-shop.myshopify.com×tamp=1648025715"; + String key = "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"; + String hmac = "803cd4924b19cedc5361ab09776d078a18be3ba32fd7d62de72269a12bec1ffc"; + assert HmacVerificationUtil.hmacSHA256(message, key, hmac); + } + + @Test + void testQuery() { + String base = "host=Z2Vlay10ZXN0LXNob3AubXlzaG9waWZ5LmNvbS9hZG1pbg×tamp=1648025715&shop=geek-test-shop.myshopify.com"; + String query = UriComponentsBuilder.fromUriString("/shopify/auth?hmac=803cd4924b19cedc5361ab09776d078a18be3ba32fd7d62de72269a12bec1ffc&host=Z2Vlay10ZXN0LXNob3AubXlzaG9waWZ5LmNvbS9hZG1pbg×tamp=1648025715&shop=geek-test-shop.myshopify.com") + .replaceQueryParam("hmac").build().getQuery(); + assertEquals(base, query); + } +} \ No newline at end of file diff --git a/src/test/java/au/com/royalpay/payment/manage/valid/ImageTest.java b/src/test/java/au/com/royalpay/payment/manage/valid/ImageTest.java index d1b72d958..97c587a80 100644 --- a/src/test/java/au/com/royalpay/payment/manage/valid/ImageTest.java +++ b/src/test/java/au/com/royalpay/payment/manage/valid/ImageTest.java @@ -1,10 +1,8 @@ package au.com.royalpay.payment.manage.valid; -import au.com.royalpay.payment.tools.env.PlatformEnvironment; import au.com.royalpay.payment.tools.exceptions.ServerErrorException; import cn.yixblog.platform.http.HttpRequestGenerator; import cn.yixblog.platform.http.HttpRequestResult; -import com.alibaba.fastjson.JSONObject; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.springframework.web.bind.annotation.RequestMethod; @@ -12,7 +10,6 @@ import org.springframework.web.bind.annotation.RequestMethod; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream;