對接華為雲市場,親測調試成功,整理一下給需要的伙伴.
接入華為雲市場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; }