Merge remote-tracking branch 'origin/develop' into develop

master
dalong306 3 years ago
commit c81da8900a

@ -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:

@ -51,6 +51,7 @@ orderWithChannel:
enum:
- Alipay
- Wechat
- AlipayPlus
type: object
required:
- channel

@ -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
<img src="img/alipayOnline_en.png">
- 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'

@ -56,6 +56,8 @@ public interface CleanService {
void settlementAba(Date date, HttpServletResponse response) throws IOException;
List<JSONObject> getCustomizedSettleDaily(Date date);
void getCustomizedSettleABA(int logId, HttpServletResponse response);
void getSettlementFilesForBatch(String batchId, HttpServletResponse resp) throws IOException;

@ -668,6 +668,15 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider
}
}
@Override
public List<JSONObject> getCustomizedSettleDaily(Date date) {
DateTime dtFrom = new DateTime(date);
DateTime dtTo = dtFrom.plusDays(1);
List<JSONObject> 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();

@ -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<JSONObject> 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);

@ -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<JSONObject> listByDate(@Param("date_from") Date dateFrom,@Param("date_to") Date dateTo);
}

@ -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());

@ -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("&timestamp=").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("&timestamp=").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);
}
}

@ -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);

@ -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);
}

@ -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())) {

@ -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

@ -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;

@ -0,0 +1,27 @@
<div class="modal-header">Manual Settlements</div>
<div class="modal-body">
<table class="table table-striped table-bordered table-hover" ng-if="customizedLogs.length">
<thead>
<tr>
<th>Merchant</th>
<th>Amount</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="log in customizedLogs">
<td ng-bind="log.client_moniker"></td>
<td ng-bind="log.amount|currency:''"></td>
<td>
<a title="Download" ng-href="{{log.aba_file}}" target="_blank"><i class="fa fa-download"></i></a>
</td>
</tr>
</tbody>
</table>
<div ng-if="!customizedLogs.length">
No manual settlements today
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger" ng-click="$dismiss()">OK</button>
</div>

@ -40,9 +40,13 @@
ng-disabled="noticeResend">
Send Settlement Notice
</button>
<button class="btn btn-primary" ng-click="showCustomizedSettle()" ng-if="'customized_settle_list'|withFunc">
<i class="fa fa-money"></i> Manual Settlements
</button>
<button class="btn btn-success pull-right" ng-click="reloadPage()">
<i class="fa fa-refresh"></i>Reload
</button>
<a ui-sref="settle_tasks" class="btn btn-primary">Settle Tasks</a>
</div>
</div>

@ -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&timestamp=1648025715";
String key = "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";
String hmac = "803cd4924b19cedc5361ab09776d078a18be3ba32fd7d62de72269a12bec1ffc";
assert HmacVerificationUtil.hmacSHA256(message, key, hmac);
}
@Test
void testQuery() {
String base = "host=Z2Vlay10ZXN0LXNob3AubXlzaG9waWZ5LmNvbS9hZG1pbg&timestamp=1648025715&shop=geek-test-shop.myshopify.com";
String query = UriComponentsBuilder.fromUriString("/shopify/auth?hmac=803cd4924b19cedc5361ab09776d078a18be3ba32fd7d62de72269a12bec1ffc&host=Z2Vlay10ZXN0LXNob3AubXlzaG9waWZ5LmNvbS9hZG1pbg&timestamp=1648025715&shop=geek-test-shop.myshopify.com")
.replaceQueryParam("hmac").build().getQuery();
assertEquals(base, query);
}
}

@ -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;

Loading…
Cancel
Save