iOS內購實現記錄

支付憑證解析工具

import javax.net.ssl.*;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Locale;


/**
 * 蘋果IAP內購驗證工具類
 */  

public class IosVerifyUtil {

    private static class TrustAnyTrustManager implements X509TrustManager {  

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[] {};
        }  

    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {  

        public boolean verify(String hostname, SSLSession session) {  

            return true;  

        }  

    }

    private static final String url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";  

    private static final String url_verify = "https://buy.itunes.apple.com/verifyReceipt";


    /**
     * 蘋果服務器驗證
     * @param receipt 支付憑證
     * @param type 1=生產環境;0=沙盒環境
     */
    public static String buyAppVerify(String receipt,int type) {  

    	//環境判斷 線上/開發環境用不同的請求鏈接
    	String url = "";
    	if(type==0){
    		url = url_sandbox; //沙盒測試
    	}else{
    		url = url_verify; //線上測試
    	}

        //String url = EnvUtils.isOnline() ?url_verify : url_sandbox;  

        try {  

            SSLContext sc = SSLContext.getInstance("SSL");  

            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());  

            URL console = new URL(url);  

            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();  

            conn.setSSLSocketFactory(sc.getSocketFactory());  

            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());  

            conn.setRequestMethod("POST");  

            conn.setRequestProperty("content-type", "text/json");  

            conn.setRequestProperty("Proxy-Connection", "Keep-Alive");  

            conn.setDoInput(true);  

            conn.setDoOutput(true);  

            BufferedOutputStream hurlBufOus = new BufferedOutputStream(conn.getOutputStream());  

            String str = String.format(Locale.CHINA, "{\"receipt-data\":\"" + receipt + "\"}");//拼成固定的格式傳給平臺

            hurlBufOus.write(str.getBytes());  

            hurlBufOus.flush();

            InputStream is = conn.getInputStream();  

            BufferedReader reader = new BufferedReader(new InputStreamReader(is));  

            String line = null;  

            StringBuffer sb = new StringBuffer();  

            while ((line = reader.readLine()) != null) {  

                sb.append(line);  

            }

            return sb.toString();  

        } catch (Exception ex) {  

        	System.out.println("蘋果服務器異常");

            ex.printStackTrace();  

        }  

        return null;  

    }

    /**
     * 用BASE64加密
     *
     * @param str
     * @return
     */
    public static String getBASE64(String str) {  

        byte[] b = str.getBytes();  

        String s = null;  

        if (b != null) {  

            s = new sun.misc.BASE64Encoder().encode(b);  

        }  

        return s;  

    }

} 

下單和回調接口

import com.alibaba.fastjson.JSONObject;
import plugins.pay.ios.IosVerifyUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;

@RestController
@Api(tags="支付接口")
@RequestMapping("/Indent")
public class H5IndentController {

    @GetMapping("/addIOS")
    @ApiOperation(value = "iOS內購新增訂單", notes = "iOS內購新增訂單")
    public String addWechat(HttpServletRequest request, HttpServletResponse response){

        // TODO 保存數據


        return "商戶訂單id";
    }

    /**
     * 蘋果內購支付回調
     * @param TransactionID 內購項目編號,不用管(感覺應該是每條支付憑證的一個唯一id,可以用來確定訂單是否處理)
     * @param Payload BASE64的驗證字符串
     * @param indentId 數據庫訂單id
     */
    @PostMapping("/iosNotify")
    public int doIosRequest(String TransactionID, String Payload, Long indentId) throws Exception {

        // 解析支付憑證
        String verifyResult =  IosVerifyUtil.buyAppVerify(Payload,1);

        if (verifyResult == null) {
            return 10010;
        } else {

            // 獲取支付憑證狀態
            JSONObject job = JSONObject.parseObject(verifyResult);
            String states = job.getString("status");

            // 沙盒環境
            if("21007".equals(states)){

                // 獲取沙盒環境憑證信息
                verifyResult =  IosVerifyUtil.buyAppVerify(Payload,0);
                job = JSONObject.parseObject(verifyResult);
                states = job.getString("status");
            }

            // 處理訂單
            if (states.equals("0")) {
                String receipt = job.getString("receipt");
                JSONObject returnJson = JSONObject.parseObject(receipt);
                String inApp = returnJson.getString("in_app");
                List<HashMap> inApps = JSONObject.parseArray(inApp, HashMap.class);
                if (!CollectionUtils.isEmpty(inApps)) {

                    // 循環處理訂單
                    for (HashMap app: inApps) {

                        // 查詢交易是否處理
                        String transactionId = (String) app.get("transaction_id");
                        // TODO 獲取到訂單id後,需要自行判斷這個訂單是否處理,transactionId這個值每個訂單時獨立的,但是iOS的
                        //  回調每次會把所有的訂單都返給回調接口,所有我沒要進行判斷

                        // TODO 處理業務邏輯
                        updateIndent(indentId + "", transactionId);
                    }
                    return 200;
                }
                return 10011;
            } else {
                return 10012;
            }

        }

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章