华为支付
华为支付不像微信/支付宝支付那样的流程,而是直接由客户端APP进行发起的支付回调。
华为支付官网地址
个人支付案例git地址[微信支付/支付宝支付/华为支付/苹果支付/小米支付]:https://gitee.com/wazk2008/demo-pay
支付回调
yml配置
huawei:
appid: 自己的appid
client-secret: 自己的app对应的密钥
回调参数
@Data
public class PayHuaweiCallbackRequest implements Serializable {
private String inAppDataSignature;
private String inAppPurchaseData;
}
@Data
public class InAppPurchaseData implements Serializable {
// (必要参数) 消耗型商品/非消耗型商品
private Boolean autoRenewing;
// (必要参数) 商户订单ID,唯一标识一次交易,由华为应用内支付服务器在支付时生成。
private String orderId;
// 应用安装包名。
private String packageName;
// (必要参数) 应用ID
private Long applicationId;
// (必要参数) 商品类别,取值包括:0:消耗型商品 1:非消耗型商品 2:订阅型商品
private Integer kind;
// (必要参数) 商品ID。每种商品必须有唯一的ID,由应用在PMS中维护,或者应用发起购买时传入。
// 即 local_transaction_id 本地支付流水号
private String productId;
private String productName;
// 商品购买时间的时间戳,即自1970年1月1日0时起到商品购买时间的毫秒数。
private Long purchaseTime;
// (必要参数) 订单交易状态。-1:初始化 0:已购买 1:已取消 2:已退款
private Integer purchaseState;
// (必要参数) 用于唯一标识商品和用户对应关系的购买令牌,在支付完成时由华为应用内支付服务器生成。
private String purchaseToken;
// 消费状态,仅一次性商品存在,取值包括: 0:未消费 1:已消费
private String consumptionState;
private String currency;
private Long price;
private String country;
// (必要参数) 商户订单ID,唯一标识一次交易,由华为应用内支付服务器在支付时生成。
// 即 third_transaction_id 第三方支付流水号
private String payOrderId;
private String payType;
private Integer accountFlag;
}
回调Controller
@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class PayController {
@Autowired
private HuaweiService huaweiService;
@PostMapping(value = "/huaweiPayCallback")
public Result<Object> payHuaweiCallback (@RequestBody PayHuaweiCallbackRequest payHuaweiCallbackRequest) {
try {
log.info("华为支付回调接口开始执行,请求参数:{}", JSON.toJSON(payHuaweiCallbackRequest));
huaweiService.payCallback(payHuaweiCallbackRequest);
log.info("华为支付回调接口执行成功,请求参数:{}", JSON.toJSON(payHuaweiCallbackRequest));
return Result.getSuccess();
} catch (ResponseException e) {
log.error("华为支付回调接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSON(payHuaweiCallbackRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(e.getCode(), e.getMessage());
} catch (Exception e) {
log.error("华为支付回调接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSON(payHuaweiCallbackRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
}
}
}
回调Service
@Service
public class HuaweiService {
private static final String ACCESS_TOKEN_URL = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
private static final String TOKEN_VERIFY_HOST1 = "https://orders-drcn.iap.hicloud.com";
private static final String TOKEN_VERIFY_HOST2 = "https://orders-at-dre.iap.dbankcloud.com";
private static final String TOKEN_VERIFY_URL = "/applications/purchases/tokens/verify";
@Value("${huawei.appid}")
private String APP_ID;
@Value("${huawei.client-secret}")
private String CLIENT_SECRET;
public void payCallback (PayHuaweiCallbackRequest payHuaweiCallbackRequest) throws Exception {
// 校验 inAppPurchaseData
InAppPurchaseData inAppPurchaseData = JSON.parseObject(payHuaweiCallbackRequest.getInAppPurchaseData(), InAppPurchaseData.class);
Integer accountFlag = inAppPurchaseData.getAccountFlag();
String productId = inAppPurchaseData.getProductId();
String purchaseToken = inAppPurchaseData.getPurchaseToken();
if (!verifyToken(purchaseToken, productId, accountFlag)) {
throw new ResponseException(ResultCode.HUAWEI_PAY_VERIFY_PURCHASE_TOKEN_ERROR);
}
// todo 校验订单是否存在
String outTradeNo = inAppPurchaseData.getProductId();
// todo 校验订单是否已支付
// todo 校验支付金额
Double buyerPayAmount = Double.valueOf(inAppPurchaseData.getPrice()) / 100;
// todo 修改支付和订单的状态
}
private boolean verifyToken(String purchaseToken, String productId, Integer accountFlag) throws Exception {
String appAt = getAppAT();
Map<String, String> headers = buildAuthorization(appAt);
Map<String, String> bodyMap = new HashMap<>();
bodyMap.put("purchaseToken", purchaseToken);
bodyMap.put("productId", productId);
String msgBody = JSONObject.toJSONString(bodyMap);
String host = TOKEN_VERIFY_HOST1;
if (!Objects.isNull(accountFlag) && accountFlag==1) {
host = TOKEN_VERIFY_HOST2;
}
String response = httpPost(host + TOKEN_VERIFY_URL, "application/json; charset=UTF-8", msgBody, 5000, 5000, headers);
JSONObject jsonRes = JSON.parseObject(response, JSONObject.class);
return !Objects.isNull(jsonRes) && "0".equals(jsonRes.get("responseCode"));
}
private String getAppAT() throws Exception {
String grantType = "client_credentials";
String msgBody = MessageFormat.format("grant_type={0}&client_secret={1}&client_id={2}", grantType, URLEncoder.encode(CLIENT_SECRET, "UTF-8"), APP_ID);
String response = httpPost(ACCESS_TOKEN_URL, "application/x-www-form-urlencoded; charset=UTF-8", msgBody, 5000, 5000, null);
JSONObject obj = JSONObject.parseObject(response);
return obj.getString("access_token");
}
private Map<String, String> buildAuthorization(String appAt) {
String oriString = MessageFormat.format("APPAT:{0}", appAt);
String authorization = MessageFormat.format("Basic {0}", Base64.encodeBase64String(oriString.getBytes(StandardCharsets.UTF_8)));
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", authorization);
headers.put("Content-Type", "application/json; charset=UTF-8");
return headers;
}
private String httpPost(String httpUrl, String contentType, String data, int connectTimeout, int readTimeout,
Map<String, String> headers) throws IOException {
OutputStream output = null;
InputStream in = null;
HttpURLConnection urlConnection = null;
BufferedReader bufferedReader = null;
InputStreamReader inputStreamReader = null;
try {
URL url = new URL(httpUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setRequestProperty("Content-Type", contentType);
if (headers != null) {
for (String key : headers.keySet()) {
urlConnection.setRequestProperty(key, headers.get(key));
}
}
urlConnection.setConnectTimeout(connectTimeout);
urlConnection.setReadTimeout(readTimeout);
urlConnection.connect();
// POST data
output = urlConnection.getOutputStream();
output.write(data.getBytes("UTF-8"));
output.flush();
// read response
if (urlConnection.getResponseCode() < 400) {
in = urlConnection.getInputStream();
} else {
in = urlConnection.getErrorStream();
}
inputStreamReader = new InputStreamReader(in, "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder strBuf = new StringBuilder();
String str;
while ((str = bufferedReader.readLine()) != null) {
strBuf.append(str);
}
return strBuf.toString();
} finally {
if (bufferedReader != null) {
bufferedReader.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (in != null) {
in.close();
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
}