对接华为云市场,亲测调试成功,整理一下给需要的伙伴.
接入华为云市场saas类商品实现新购商品 商品续费 商品过期 商品资源释放 商品升级
官方文档:https://support.huaweicloud.com/accessg-marketplace/zh-cn_topic_0070649013.html

目录结构:
pojo 请求与响应实体
package com.a.b.isv.pojo;
import java.io.Serializable;
import java.math.BigDecimal;
import com.a.b.isv.domain.ActivityEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 商品实例通知
*
* @author l
* @date 2021/04/01
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class IsvInstance implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 实例ID(新购场景无实例ID,其余场景必须要有)
*/
private String instanceId;
/**
* 安全校验令牌
*/
private String authToken;
/**
* 请求发起时的时间戳,取UTC时间。格式:yyyyMMddHHmmssSSS
*/
private String timeStamp;
/**
* 接口请求标识,用于区分接口请求场景
*/
private ActivityEnum activity;
/**
* 客户在华为云注册账号的唯一标识
*/
private String customerId;
/**
* 客户在华为云注册的账户名
*/
private String customerName;
/**
* 客户以IAM用户认证方式登录时对应子用户的唯一标识。
* 非必传,如需此参数,在商品发布时“需要用户授权”请选择“基于IAM用户名创建应用管理账号等信息”。
*/
private String userId;
/**
* 客户以IAM用户认证方式登录的用户名。
* 非必传,如需此参数,在商品发布时“需要用户授权”请选择“基于IAM用户名创建应用管理账号等信息”。
*/
private String userName;
/**
* 客户手机号,不包含国家码。
* 非必传,如需此参数,在商品发布时“需要用户授权”请选择“基于手机号码创建应用管理账号等信息”,取值为加密后的手机号码。
* 手机号加密规则如下:
* 由16位iv加密向量和base编码后的手机号密文组成。
* iv+base64(AES_CBC(accessKey,mobilePhone))
* 加密位数取ISV发布产品时选择的加密位数。
*/
private String mobilePhone;
/**
* 客户邮箱。
* 非必传,如需此参数,在商品发布时“需要用户授权”请选择“基于邮箱创建应用管理账号等信息”,取值为加密后的邮箱。
* 邮箱加密规则如下:
* 由16位iv加密向量和base编码后的邮箱密文组成。
* iv+base64(AES_CBC(accessKey,email))
* 加密位数取ISV发布产品时选择的加密位数。
*/
private String email;
/**
* 云市场业务ID。
* 每一次请求,businessId皆不一致。
*/
private String businessId;
/**
* 云市场订单ID。
*/
private String orderId;
/**
* 产品规格标识。租户购买包月或包年的产品后,可能会续费,续费支持变更周期类型(例如包月转包年),
* 此时,租户开通的实例instanceId对应的productId会变化,但skuCode不变。
* 该参数可在商品审核上架后,进入“卖家中心 > 商品管理 > 我的商品 ”页面,单击该商品操作列的“详情”进入商品详情页面获取
*/
private String skuCode;
/**
* 产品标识,同一skuCode下,不同周期类型的productId不同。
* 例如:ISV发布产品,新增一个规格,会生成一个skuCode,再配置包年价格,包月价格,会生成两个productId。
* 该参数可在商品审核上架后,进入“卖家中心 > 商品管理 > 我的商品 ”页面,单击该商品操作列的“详情”进入商品详情页面获取。
*/
private String productId;
/**
* 是否为调试请求。
* 1:调试请求
* 0:非调试请求
* 取值为“0”时默认不传。
*/
private String testFlag;
/**
* 是否是开通试用实例。
* 1:试用实例
* 0:非试用实例
* 不传试用参数:2018年5月12日之前已发布成功的产品实例
* 取值为“0”时默认不传。
*/
private String trialFlag;
/**
* 过期时间。
* 格式:yyyyMMddHHmmss
* 按周期售卖的商品,会请求该参数。
* 按次售卖的商品,不会请求该参数。
* 过期时间根据订单创建时间和购买周期计算而来,与订单实际过期时间有误差,仅供参考。
*/
private String expireTime;
/**
* 计费模式。
* 3:表示按次购买。
* 说明:
* 包周期购买场景请求时不传该参数。
* 按次购买场景请求时传该参数。
*/
private Integer chargingMode;
/**
* 扩展参数。非必填。
* 扩展参数格式为json数组字符串通过 urlEncode(base64(saasExtendParams))携带到url参数中。在得到saasExtendParams参数的值后,需要通过base64Decode(urlDecode(saasExtendParams))获取扩展参数json数组。
* 例如:[{"name":"emailDomainName","value":"test.xxxx.com"},{"name":"extendParamName","value":"extendParamValue"}]
* 其中emailDomainName和extendParamName为发布商品时填写值。
* 说明:
* (本说明仅适用于WeLink开放平台开发的商品)
* 请先在应用接入调试页面调测“WeLink商品接口调测必选参数”,发布WeLink开放平台开发的商品时,会包含name值为platformParams的扩展参数,值为json格式字符串,包含tenantName、tennantId、userId三个参数。
* tenantName是WeLink中的企业名
* tennantId是WeLink中的企业唯一标识符
* userId是WeLink中订阅该应用的用户,通常为企业管理员(WeLink企业管理员可以有多个且企业管理员账号可被注销)
*/
private String saasExtendParams;
/**
* 数量类型的商品定价属性。非必填。
* 属性名称:数量(支持服务商自定义名称)
* 单位:个(次)
* 说明:
* 对于包周期或一次性计费的SaaS商品,租户下单购买包含“数量”线性属性的规格时,会填写及调整购买的个数或次数。
* 例如:30个用户
*/
private Integer amount;
/**
* 数量类型的商品定价属性。非必填。
* 属性名称:硬盘大小(支持服务商自定义名称)
* 单位:GB
* 说明:
* 对于包周期或一次性计费的SaaS商品,租户下单购买包含“硬盘大小”线性属性的规格时,会填写及调整购买多少GB。
* 例如:100GB
*/
private Integer diskSize;
/**
* 数量类型的商品定价属性。非必填。
* 属性名称:带宽(支持服务商自定义名称)
* 单位:Mbps
* 说明:
* 对于包周期或一次性计费的SaaS商品,租户下单购买包含“带宽”线性属性的规格时,会填写及调整购买多少Mbps。
* 例如:20Mbps
*/
private Integer bandWidth;
/**
* 周期类型。
* 说明:
* 非必传,如需此参数,计费类型需选择包周期chargingMode=1,包周期购买场景请求时传该参数。
* 年:"year"
* 月:"month"
*/
private String periodType;
/**
* 周期数量。
* 说明:
* 非必传,如需此参数,计费类型需选择包周期chargingMode=1,包周期购买场景请求时传该参数。
* 周期数量:1,2,3…
*/
private Integer periodNumber;
/**
* 订单金额。
* 说明:
* 该金额为用户实际支付金额,供服务商对账参考。
* 金额值大于等于0,最大三位小数。
* 单位:元
*/
private BigDecimal orderAmount;
/**
* 商品实例开通方式。
* 说明:
* 用户购买后同步开通(默认,云市场轮询调用newInstance生产接口)
* 用户确认验收后开通(SaaS涉及服务监管)
* 用户购买该商品时,云市场调用新购商品接口,服务商需返回结果码为请求处理中000004或订单创建成功000000。
* 当服务商点击开通交付时,云市场调用新购商品接口,服务商需返回结果码成功000000。
* 用户点击确认验收时,云市场调用新购商品传入用户验收时间给到服务商,服务商需返回结果码成功000000。
* ISV接口通知云市场开通(暂未使用)
*/
private Integer provisionType;
/**
* 用户验收时间。
* 说明:
* 此时间为用户计费开始时间,如选择的商品实例开通方式为“用户确认验收后开通”,则用户验收时间为必填项。
* 格式:yyyyMMddHHmmssSSS
*/
private String acceptanceTime;
}
appInfo
package com.a.b.isv.pojo;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 应用实例信息。
* 客户购买商品后,服务商需要返回登录服务地址(网站地址)或免登地址供客户后续操作。
*
* @author y
* @date 2021/04/02
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class AppInfo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 前台地址,客户购买商品后,可以访问的网站地址。
*/
private String frontEndUrl;
/**
* 管理地址,客户购买商品后,可以访问的管理后台地址。
*/
private String adminUrl;
/**
* 加密后的管理员帐号。
* 客户购买商品后,访问服务商管理后台的账号(一般为邮箱和手机号)。该值由16位iv加密向量和base编码后的用户名密文组成。
* iv+base64(AES_CBC(accessKey,userName))
* 需要使用Key值对密码做加密处理,加密算法以encryptType参数为准。
*/
private String userName;
/**
* 加密后的管理员初始密码。
* 客户购买商品后,访问服务商管理后台的密码(一般由服务商生成)。该值由16位iv加密向量和base编码后的密码密文组成。
* iv+base64(AES_CBC(accessKey,pwd))
* 需要使用Key值对密码做加密处理,加密算法以encryptType参数为准。
*/
private String password;
/**
* 备注。
* 说明:
* 如果备注包含中文内容,请将中文转换成unicode编码,例如:“中文”可以转换成“\u4e2d\u6587”。
*/
private String memo;
}
ResponseMsg
package com.a.b.isv.core;
import java.io.Serializable;
import com.a.b.isv.domain.ResultCodeEnum;
import com.a.b.isv.pojo.AppInfo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 响应消息
*
* @author y
* @date 2021/04/02
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ResponseMsg implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 调用结果码。
*/
private String resultCode;
/**
* 调用结果描述。
*/
private String resultMsg;
/**
* 应用实例信息。
* 客户购买商品后,服务商需要返回登录服务地址(网站地址)或免登地址供客户后续操作。
* 敏感信息加密算法
* 1:AES256_CBC_PKCS5Padding(默认值)
* 2:AES128_CBC_PKCS5Padding
*/
private String encryptType = "1";
/**
* 实例ID,服务商提供的唯一标识。
*/
private String instanceId;
/**
* 应用实例信息。
* 客户购买商品后,服务商需要返回登录服务地址(网站地址)或免登地址供客户后续操作。
*/
private AppInfo appInfo;
public void setResultCode(ResultCodeEnum resultCodeEnum) {
this.resultCode = resultCodeEnum.getCode();
this.resultMsg = resultCodeEnum.getLiteral();
}
}
ActivityEnum
package com.a.b.isv.domain;
/**
* 接口请求标识,用于区分接口请求场景。
*
* @author y
* @date 2021/04/01
*/
public enum ActivityEnum {
newInstance("新购商品"),
refreshInstance("续费"),
expireInstance("商品过期"),
releaseInstance("商品资源释放"),
upgrade("商品升级");
private final String literal;
ActivityEnum(String literal) {
this.literal = literal;
}
}
ResultCodeEnum
package com.a.b.isv.domain;
/**
* 响应码
*
* @author y
* @date 2021/04/01
*/
public enum ResultCodeEnum {
success("000000", "success", "公共"),
authFailed("000001", "鉴权失败", "公共"),
parameterInvalid("000002", "请求参数不合法", "公共"),
instanceIDNotExist("000003", "实例ID不存在", "公共"),
processing("000004", "请求处理中", "公共"),
othersFailed("000005", "其它服务内部错误", "公共"),
noEnoughResources("000100", "无可用实例资源分配", "新购商品");
private final String code;
private final String literal;
private final String module;
ResultCodeEnum(String code, String literal, String module) {
this.code = code;
this.literal = literal;
this.module = module;
}
public String getCode() {
return code;
}
public String getLiteral() {
return literal;
}
public String getModule() {
return module;
}
@Override
public String toString() {
return code;
}
}
加解密工具
package com.a.b.isv.util;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;
import cn.hutool.json.JSONObject;
/**
* 加解密工具
*
* @author y
* @date 2021/04/02
*/
public class SecureUtils {
/**
* 校验通知消息的合法性
*
* @param paramsMap 请求通知消息
* @param accessKey 接入码
* @param encryptLength 加密长度
* @return 验证结果
*/
public static boolean verificationRequestParams(Map<String, Object> paramsMap, String accessKey, int encryptLength) {
//解析出url内容
String timeStamp = (String) paramsMap.get("timeStamp");
String authToken = (String) paramsMap.get("authToken");
//对剩下的参数进行排序,拼接成加密内容
Map<String, Object> sortedMap = new TreeMap<>(paramsMap);
sortedMap.remove("authToken");
StringBuilder strBuffer = new StringBuilder();
Set<String> keySet = sortedMap.keySet();
for(String key : keySet) {
String value = String.valueOf(sortedMap.get(key));
strBuffer.append("&")
.append(key)
.append("=")
.append(value);
}
//修正消息体,去除第一个参数前面的&
String reqParams = strBuffer
.substring(1);
String key = accessKey + timeStamp;
String signature = null;
try {
signature = generateResponseBodySignature(key, reqParams);
} catch(InvalidKeyException | NoSuchAlgorithmException
| IllegalStateException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
}
return authToken.equals(signature);
}
/**
* 生成http响应消息体签名示例Demo
*
* @param key 用户在isv console分配的accessKey,请登录后查看
* @param body http响应的报文
* @return 加密结果
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws IllegalStateException
* @throws UnsupportedEncodingException
*/
public static String generateResponseBodySignature(String key, String body) throws InvalidKeyException,
NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException {
return base64(hmacSHA256(key, body));
}
public static byte[] hmacSHA256(String macKey, String macData)
throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException {
SecretKeySpec secret =
new SecretKeySpec(macKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secret);
return mac.doFinal(macData.getBytes(StandardCharsets.UTF_8));
}
/**
* 字节数组转字符串
*
* @param bytes 字节数组
* @return 字符串
*/
public static String base64(byte[] bytes) {
return new String(Base64.encodeBase64(bytes));
}
/**
* 对资源开通后,返回的用户名和密码进行加密
*
* @param key 秘钥
* @param str 原文
* @param encryptLength 加密长度
* @return 加密结果
*/
public static String generateSaaSUsernameOrPwd(String key, String str, int encryptLength) {
String iv = getRandomChars(16);
String afterEncryptStr = "";
try {
afterEncryptStr = encryptAESCBCEncode(str, key, iv, encryptLength);
} catch(InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | InvalidAlgorithmParameterException
| IllegalBlockSizeException | BadPaddingException e) {
//TODO:异常处理
}
return iv + afterEncryptStr;
}
/**
* 随机生成字符串
*
* @param length 随机字符串的长度
* @return 随机字符串
*/
public static String getRandomChars(int length) {
String randomChars = "";
SecureRandom random = new SecureRandom();
for(int i = 0; i < length; i++) {
//字母和数字中随机
if(random.nextInt(2) % 2 == 0) {
//输出是大写字母还是小写字母
int letterIndex = random.nextInt(2) % 2 == 0 ? 65 : 97;
randomChars += (char) (random.nextInt(26) + letterIndex);
} else {
randomChars += String.valueOf(random.nextInt(10));
}
}
return randomChars;
}
/**
* AES CBC 位加密
*
* @param content 加密内容
* @param key 加密秘钥
* @param iv 向量iv
* @param encryptLength 仅支持128、256长度
* @return 加密结果
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws InvalidAlgorithmParameterException
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static String encryptAESCBCEncode(String content, String key, String iv, int encryptLength)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {
if(StringUtils.isEmpty(content) || StringUtils.isEmpty(key) || StringUtils.isEmpty(iv)) {
return null;
}
return base64(encryptAESCBC(content.getBytes(StandardCharsets.UTF_8), key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8), encryptLength));
}
/**
* AES CBC 256位加密
*
* @param content 加密内容字节数组
* @param keyBytes 加密字节数组
* @param iv 加密向量字节数组
* @param encryptLength 仅支持128、256长度
* @return 解密后字节内容
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public static byte[] encryptAESCBC(byte[] content, byte[] keyBytes, byte[] iv, int encryptLength)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(keyBytes);
keyGenerator.init(encryptLength, secureRandom);
SecretKey key = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] result = cipher.doFinal(content);
return result;
}
/**
* 解密手机号码或邮箱
*
* @param key 秘钥
* @param str 密文
* @param encryptLength 加密长度
* @return 解密结果
*/
public static String decryptMobilePhoneOrEMail(String key, String str, int encryptLength) {
if(null != str && str.length() > 16) {
String iv = str.substring(0, 16);
String encryptStr = str.substring(16);
String result = null;
try {
result = decryptAESCBCEncode(encryptStr, key, iv, encryptLength);
} catch(InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | InvalidAlgorithmParameterException
| IllegalBlockSizeException | BadPaddingException e) {
//TODO:异常处理
}
return result;
}
return null;
}
/**
* 解密AES CBC
*
* @param content 原文
* @param key 秘钥
* @param iv 盐值
* @return 解密结果
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws InvalidAlgorithmParameterException
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static String decryptAESCBCEncode(String content, String key, String iv, int encryptType)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
if(StringUtils.isEmpty(content) || StringUtils.isEmpty(key)
|| StringUtils.isEmpty(iv)) {
return null;
}
return new String(decryptAESCBC(Base64.decodeBase64(content.getBytes()),
key.getBytes(),
iv.getBytes(), encryptType));
}
public static byte[] decryptAESCBC(byte[] content, byte[] keyBytes, byte[] iv, int encryptType)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(keyBytes);
keyGenerator.init(encryptType, secureRandom);
SecretKey key = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(content);
}
}
controller
package com.a.b.isv.producer.controller.anonymous;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.a.b.isv.conf.IsvConfig;
import com.a.b.isv.core.ResponseMsg;
import com.a.b.isv.pojo.IsvInstance;
import com.a.b.isv.service.IsvService;
import com.a.b.isv.util.SecureUtils;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* 接入华为云市场实现新购商品 商品续费 商品过期 商品资源释放 商品升级
* 文档:https://support.huaweicloud.com/accessg-marketplace/zh-cn_topic_0070649013.html
*
* @author*/
@Api(tags = "saas商品接入")
@RequestMapping("isv/")
@RestController
@AllArgsConstructor
@Slf4j
public class IsvController {
private final IsvService isvService;
private final IsvConfig isvConfig;
@ApiOperation("saas商品接入,华为云市场在客户购买商品后,将请求本接口")
@GetMapping("marketplace/saas")
public String instance(IsvInstance instance, HttpServletResponse response) {
ResponseMsg responseBody = isvService.instance(instance);
String signature = null;
String jsonStr = null;
try {
// service返回的响应转换为json字符串
jsonStr = JSONUtil.toJsonStr(responseBody);
// 对响应json字符串进行签名
signature = SecureUtils.generateResponseBodySignature(isvConfig.getAccessKey(), jsonStr);
} catch(Exception e) {
log.error("生成响应body签名异常:{}", ExceptionUtil.getMessage(e));
}
// 签名放入HttpServletResponse 响应头
response.addHeader("Body-Sign", "sign_type=\"HMAC-SHA256\"" + "," + "signature=" + "\"" + signature + "\"");
return jsonStr;
}
}
service
package com.a.b.isv.service;
import java.util.Objects;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import com.a.b.isv.conf.IsvConfig;
import com.a.b.isv.core.ResponseMsg;
import com.a.b.isv.domain.ActivityEnum;
import com.a.b.isv.domain.ResultCodeEnum;
import com.a.b.isv.pojo.AppInfo;
import com.a.b.isv.pojo.IsvInstance;
import com.a.b.isv.util.SecureUtils;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.thread.ThreadUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* SaaS商品接入云市场
*
* @author y
* @date 2021/03/22
*/
@Service
@AllArgsConstructor
@Slf4j
public class IsvService {
// 配置文件包含卖家中心获取的key值,和各类配置信息 https://support.huaweicloud.com/accessg-marketplace/zh-cn_topic_0070649065.html
private final IsvConfig isvConfig;
public ResponseMsg instance(IsvInstance isvInstance) {
ResponseMsg responseBody = new ResponseMsg();
// 校验通知消息的合法性
boolean verification = SecureUtils.verificationRequestParams(
BeanUtil.beanToMap(isvInstance, false, true)
, isvConfig.getAccessKey(), -1);
if(!verification) {
responseBody.setResultCode(ResultCodeEnum.authFailed);
return responseBody;
}
// 业务逻辑处理,此处需要根据自己公司产品进行相应的代码编写
// 响应
responseBody.setInstanceId(isvInstance.getBusinessId());
responseBody.setResultCode(ResultCodeEnum.success);
AppInfo appInfo = new AppInfo();
appInfo.setFrontEndUrl("http://front.a.com/b/index.html#/user/login");
appInfo.setAdminUrl("http://front.a.com/b/index.html#/user/login");
appInfo.setUserName(SecureUtils.generateSaaSUsernameOrPwd(isvConfig.getAccessKey(),"user1000",256));
appInfo.setPassword(SecureUtils.generateSaaSUsernameOrPwd(isvConfig.getAccessKey(),"user1000",256));
appInfo.setMemo("hello world");
responseBody.setAppInfo(appInfo);
return responseBody;
}
/**
* 新购场景
*/
private void newInstanceScene(IsvInstance isvInstance) {
log.info("新购场景:");
}
/**
* 续费场景
*/
private void refreshInstanceScene() {
log.info("续费场景:");
}
/**
* 商品过期
*/
private void expireInstanceScene() {
log.info("商品过期场景:");
}
/**
* 商品资源释放
*/
private void releaseInstanceScene() {
log.info("商品资源释放场景:");
}
/**
* 商品升级
*/
private void upgradeScene() {
log.info("商品升级场景:");
}
/**
* 实例是否已存在,并且有效
*
* @param orderId
* @return
*/
private boolean isExist(String orderId) {
return true;
}
}
IsvConfig
@lombok.Getter
@lombok.Setter
@Configuration
@ConfigurationProperties("huawei.isv")
public class IsvConfig {
/**
* accessKey 登录华为云市场卖家中心查看
*/
private String accessKey;
/**
* 前台地址,客户购买商品后,可以访问的网站地址。
*/
private String frontEndUrl;
/**
* 管理地址,客户购买商品后,可以访问的管理后台地址。
*/
private String adminUrl;
/**
* 备注。
* 说明:
* 如果备注包含中文内容,请将中文转换成unicode编码,例如:“中文”可以转换成“\u4e2d\u6587”。
*/
private String memo;
}