微信登陸接入(Android/IOS(swift)/Java后臺(tái))

本文章僅作為個(gè)人筆記

微信Android接入指南,需要登陸后查看(開(kāi)放平臺(tái)->資源中心->開(kāi)發(fā)資源->移動(dòng)應(yīng)用->接入指南)
微信IOS接入指南俘枫,需要登陸后查看(開(kāi)放平臺(tái)->資源中心->開(kāi)發(fā)資源->移動(dòng)應(yīng)用->接入指南)
微信開(kāi)放平臺(tái)
  • 首先要注冊(cè)成為微信開(kāi)放平臺(tái)用戶
  • 通過(guò)開(kāi)發(fā)者資質(zhì)認(rèn)證
    • 提供個(gè)人信息
    • 提供公司營(yíng)業(yè)執(zhí)照等信息
    • 300rmb
  • 創(chuàng)建移動(dòng)應(yīng)用并通過(guò)審核
    • 提供應(yīng)用描述
    • 提供應(yīng)用logo
    • 提供應(yīng)用安裝包
    • 提供應(yīng)用簽名信息(建議下載官方簽名獲取apk獲取簽名提交囱晴,另外萬(wàn)一簽名錯(cuò)誤導(dǎo)致登錄時(shí)返回-6錯(cuò)誤碼箱季,網(wǎng)頁(yè)更改簽名后需要清理客戶端微信緩存再重新嘗試裸删。)
  • 萬(wàn)事具備后可以查看到應(yīng)用的AppID和AppSecret
  • 開(kāi)始代碼模塊
  • IOS端(swift):
    • 導(dǎo)入第三方庫(kù)(cocopods集成)(記得 pod install):

      pod 'WechatOpenSDK'
      
    • 引用:在橋接文件中添加以下代碼(橋接文件為項(xiàng)目根目錄下的.h文件,如果沒(méi)有可創(chuàng)建并添加)

      #import "WXApi.h"
      
    • 部分設(shè)置:


      image.png

      image.png

      打開(kāi)項(xiàng)目info.plist换帜,加入如下代碼(主要是NSAllowsArbitraryLoads及其值還有就是weixin/wechat):

          <key>NSAppTransportSecurity</key>
          <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
          </dict>
          <key>LSApplicationQueriesSchemes</key>
          <array>
            <string>weixin</string>
            <string>wechat</string>
          </array>
      
改完info.plist結(jié)果如圖
  • 等所有配置完畢即可進(jìn)行編程了楔壤。

    • 先貼上AppDelegate部分主要代碼

            @UIApplicationMain
            class AppDelegate: UIResponder, UIApplicationDelegate {
            
                var wechatAuthBack: HttpUtilsBack?
                var wechatPayBack: HttpUtilsBack?
            
                func application(_ application: UIApplication, didFinishLaunchingWithOptions
                launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
                    _ = WXApi.registerApp(StaticParam.WECHART_APPID)//appid字符串
                    return true
                }
            
                func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
                    switch url.scheme {
                    case StaticParam.WECHART_APPID:
                        _ = WXApi.handleOpen(url, delegate: self)
                    default:
                        print("handleOpenUrl1")
                    }
                    return true
                }
            
                func application(_ application: UIApplication, open url: URL
                        , sourceApplication: String?, annotation: Any) -> Bool {
                    switch url.scheme {
                    case StaticParam.WECHART_APPID:
                        _ = WXApi.handleOpen(url, delegate: self)
                    default:
                        print("handleOpenUrl2")
                    }
                    return true
                }
            
            }
            
            extension AppDelegate: WXApiDelegate {
            
                func onReq(_ req: BaseReq!) {
                }
            
                func onResp(_ resp: BaseResp!) {
                    var code: String?
                    var error: BaseError?
                    if resp.isKind(of: SendAuthResp.self) {
                        let authResp = resp as! SendAuthResp
                        if authResp.errCode == 0 {
                            code = authResp.code
                        } else {
                            error = BaseError(authResp.errStr)
                        }
                    } else if resp.isKind(of: PayResp.self) {
                        let payResp = resp as! PayResp
                        if payResp.errCode == 0 {
                            code = payResp.returnKey
                        } else {
                            error = BaseError(payResp.errStr)
                        }
                    }
                    if wechatAuthBack != nil {
                        if error == nil && code == nil {
                            error = "登錄失敗"
                        }
                        wechatAuthBack?.finish(result: code, error: error)
                    } else if wechatPayBack != nil {
                        if error == nil && code == nil {
                            error = "支付失敗"
                        }
                        wechatPayBack?.finish(result: code, error: error)
                    }
                }
            }
            protocol HttpUtilsBack {
                func finish(result: String?, error: BaseError?)
            }
      
    • 再貼上登錄部分主要代碼

            let req = SendAuthReq()
            req.scope = "snsapi_userinfo" //獲取用戶信息
            req.state = String(Date().timeIntervalSince1970) //隨機(jī)值即可,這里用時(shí)間戳
            WXApi.send(req)
      
    • 貼上登錄回調(diào)代碼:

            appDelegate?.wechatAuthBack = WechatAuthBack()
            //創(chuàng)建回調(diào)類
            struct WechatAuthBack: HttpUtilsBack {
                func finish(result: String?, error: BaseError?) {
                    if error == nil {
                        //登錄成功回調(diào)
                    } else {
                        //登錄失敗回調(diào)
                    }
                }
            }
      
  • 如果調(diào)用成功會(huì)回調(diào)onResp方法惯驼,后續(xù)在onResp內(nèi)處理即可蹲嚣,處于安全考慮递瑰,建議用戶信息解析給服務(wù)器端處理,這里直接將獲取的信息上傳至服務(wù)器即可

  • Android端:

    • 引入jar包(在build.gradle文件內(nèi))

      dependencies {
        api 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'
      }
      
    • 添加必要權(quán)限

      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
      <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
      <uses-permission android:name="android.permission.READ_PHONE_STATE" />
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      
    • 登錄相關(guān)代碼

      //初始化IWXAPI實(shí)例
      IWXAPI api = WXAPIFactory.createWXAPI(activity, appId, true);
      api.registerApp(appId);
      //初始化登錄請(qǐng)求對(duì)象
      SendAuth.Req req = new SendAuth.Req();
      req.scope = "snsapi_userinfo";
      req.state = String.valueOf(System.currentTimeMillis());
      //發(fā)送登錄請(qǐng)求
      api.sendReq(req);
      //最后記得在activity的onDestroy方法內(nèi)取消注冊(cè)IWXAPI對(duì)象
      api.unregisterApp();
      
    • 登錄結(jié)果接收類

      • 在{包名}.wxapi下創(chuàng)建WXEntryActivity類

      • 本人寫(xiě)的代碼(WXEntryActivity)如下:

              import android.app.Activity;
              import android.os.Bundle;
              import android.util.Log;
              
              import com.tencent.mm.opensdk.constants.ConstantsAPI;
              import com.tencent.mm.opensdk.modelbase.BaseReq;
              import com.tencent.mm.opensdk.modelbase.BaseResp;
              import com.tencent.mm.opensdk.modelmsg.SendAuth;
              import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
              
              public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
              
                  public static final String APP_ID = "";//這里寫(xiě)自己的appid
                  public static Back authBack;
                  private IWXAPI api;
              
                  public interface Back {
                      public void onFiled(int errorCode);
              
                      public void onSuccess(String code, String state);
                  }
              
                  public static void registAuthBack(Back back) {
                      authBack = back;
                  }
              
                  public static void unregistAuthBack() {
                      authBack = null;
                  }
              
                  @Override
                  protected void onCreate(Bundle savedInstanceState) {
                      super.onCreate(savedInstanceState);
                      api = WXAPIFactory.createWXAPI(this, appId, true);
                      api.handleIntent(this, this)
                  }
              
                  @Override
                  public void onReq(BaseReq baseReq) {
                      Log.e("-----1", "baseReq=" + baseReq);
                  }
              
                  @Override
                  public void onResp(BaseResp baseResp) {
                      switch (baseResp.getType()) {
                          case ConstantsAPI.COMMAND_PAY_BY_WX:
                              Log.e("-----1", "onPayFinish,errCode=" + baseResp.errCode);
                              // 0:成功; -1:錯(cuò)誤; -2:用戶取消;
                              break;
                          case ConstantsAPI.COMMAND_SENDAUTH:
                              Log.e("-----1", "onAuthFinish,errCode=" + baseResp.errCode);
                              if (authBack != null) {
                                  // 0:成功; -1:錯(cuò)誤; -2:用戶取消;
                                  SendAuth.Resp authResp = (SendAuth.Resp) baseResp;
                                  if (authResp.errCode == 0) {
                                      authBack.onSuccess(authResp.code, authResp.state);
                                  } else {
                                      authBack.onFiled(baseResp.errCode);
                                  }
                              }
                              break;
                      }
                      finish();
                  }
              
                  @Override
                  protected void onDestroy() {
                      super.onDestroy();
                      api.unregisterApp();
                  }
              
              }
        
      • 記得在AndroidManifest.xml文件下注冊(cè)此Activity時(shí)需使用如下格式(主要是確保有android:exported="true"屬性):

          <activity
              android:name=".wxapi.WXEntryActivity"
              android:exported="true"
              android:label="@string/app_name" />
        
      • 相信大家也看到WXEntryActivity類下有個(gè)registAuthBack方法隙畜,傳入了一個(gè)回調(diào)抖部,這個(gè)就是當(dāng)你調(diào)起微信登錄后可以調(diào)用的,獲取登錄結(jié)果议惰,返回的code字符串上傳至服務(wù)器獲取用戶信息(也可以在客戶端做慎颗,但是不安全叼耙,建議在服務(wù)器做)岩臣。

  • 服務(wù)器端:

    • 因?yàn)橹皇前l(fā)送get請(qǐng)求然后解析結(jié)果,所以直接上2個(gè)工具類:
      • 網(wǎng)絡(luò)請(qǐng)求封裝類:

          import javax.net.ssl.HttpsURLConnection;
          import java.io.ByteArrayOutputStream;
          import java.io.InputStream;
          import java.net.HttpURLConnection;
          import java.net.Proxy;
          import java.net.URL;
          import java.util.HashMap;
          import java.util.List;
          import java.util.Map;
          
          public class HttpUtils {
          
              private String HTTPS = "https";
              private String GET = "GET";
              private String POST = "POST";
              private static HttpUtils httpUtils;
          
              private HttpUtils() {
              }
          
              public static HttpUtils getInstance() {
                  if (httpUtils == null) {
                      httpUtils = new HttpUtils();
                  }
                  return httpUtils;
              }
          
              public interface IWebCallback {
          
                  void onCallback(int status, String message, Map<String, List<String>> heard, byte[] data);
          
                  void onFail(int status, String message);
          
              }
          
              public byte[] getURLResponse(String urlString, HashMap<String, String> heads) {
                  byte[] result = null;
                  if (urlString != null) {
                      HttpURLConnection conn = null; //連接對(duì)象
                      InputStream is = null;
                      ByteArrayOutputStream baos = null;
                      try {
                          URL url = new URL(urlString); //URL對(duì)象
                          if (urlString.startsWith(HTTPS)) {
                              conn = (HttpsURLConnection) url.openConnection();
                          } else {
                              conn = (HttpURLConnection) url.openConnection();
                          }
                          conn.setConnectTimeout(5 * 1000);
                          conn.setDoOutput(true);
                          conn.setRequestMethod(GET);
                          if (heads != null) {
                              for (String key : heads.keySet()) {
                                  conn.addRequestProperty(key, heads.get(key));
                              }
                          }
                          is = conn.getInputStream();   //獲取輸入流芥吟,此時(shí)才真正建立鏈接
                          baos = new ByteArrayOutputStream();
                          byte[] temp = new byte[1024];
                          int len;
                          while ((len = is.read(temp)) != -1) {
                              baos.write(temp, 0, len);
                          }
                          result = baos.toByteArray();
                      } catch (Exception e) {
                      } finally {
                          CloseUtils.closeSilently(is);
                          CloseUtils.closeSilently(baos);
                          if (conn != null) {
                              conn.disconnect();
                          }
                      }
                  }
                  return result;
              }
          
              public void getURLResponse(String urlString, HashMap<String, String> heads, IWebCallback iWebCallback) {
                  getURLResponse(urlString, heads, null, iWebCallback);
              }
          
              public void getURLResponse(String urlString, HashMap<String, String> heads, Proxy proxy, IWebCallback iWebCallback) {
                  if (urlString != null) {
                      HttpURLConnection conn = null; //連接對(duì)象
                      InputStream is = null;
                      ByteArrayOutputStream baos = null;
                      try {
                          URL url = new URL(urlString); //URL對(duì)象
                          if (proxy == null) {
                              if (urlString.startsWith(HTTPS)) {
                                  conn = (HttpsURLConnection) url.openConnection();
                              } else {
                                  conn = (HttpURLConnection) url.openConnection();
                              }
                          } else {
                              if (urlString.startsWith(HTTPS)) {
                                  conn = (HttpsURLConnection) url.openConnection(proxy);
                              } else {
                                  conn = (HttpURLConnection) url.openConnection(proxy);
                              }
                          }
                          conn.setConnectTimeout(5 * 1000);
                          conn.setDoOutput(true);
                          conn.setRequestMethod(GET);
                          if (heads != null) {
                              for (String key : heads.keySet()) {
                                  conn.addRequestProperty(key, heads.get(key));
                              }
                          }
                          is = conn.getInputStream();   //獲取輸入流倍试,此時(shí)才真正建立鏈接
                          baos = new ByteArrayOutputStream();
                          byte[] temp = new byte[1024];
                          int len;
                          while ((len = is.read(temp)) != -1) {
                              baos.write(temp, 0, len);
                          }
                          if (iWebCallback != null) {
                              iWebCallback.onCallback(conn.getResponseCode(), conn.getResponseMessage(), conn.getHeaderFields(), baos.toByteArray());
                          }
                      } catch (Exception e) {
                          int code = 600;
                          try {
                              code = conn == null ? 600 : conn.getResponseCode();
                          } catch (Exception e1) {
                          }
                          if (iWebCallback != null) {
                              iWebCallback.onFail(code, e.toString());
                          }
                      } finally {
                          CloseUtils.closeSilently(is);
                          CloseUtils.closeSilently(baos);
                          if (conn != null) {
                              conn.disconnect();
                          }
                      }
                  }
              }
          
              public byte[] postURLResponse(String urlString, HashMap<String, String> headers, byte[] postData) {
                  byte[] result = null;
                  if (urlString != null) {
                      HttpURLConnection conn = null; //連接對(duì)象
                      InputStream is = null;
                      ByteArrayOutputStream baos = null;
                      try {
                          URL url = new URL(urlString); //URL對(duì)象
                          if (urlString.startsWith(HTTPS)) {
                              conn = (HttpsURLConnection) url.openConnection();
                          } else {
                              conn = (HttpURLConnection) url.openConnection();
                          }
                          conn.setConnectTimeout(5 * 1000);
                          conn.setDoOutput(true);
                          conn.setRequestMethod(POST); //使用post請(qǐng)求
                          conn.setRequestProperty("Charsert", "UTF-8");
                          if (headers != null) {
                              for (Map.Entry<String, String> temp : headers.entrySet()) {
                                  conn.setRequestProperty(temp.getKey(), temp.getValue());
                              }
                          }
                          conn.getOutputStream().write(postData);
                          is = conn.getInputStream();   //獲取輸入流讯屈,此時(shí)才真正建立鏈接
                          baos = new ByteArrayOutputStream();
                          byte[] temp = new byte[1024];
                          int len;
                          while ((len = is.read(temp)) != -1) {
                              baos.write(temp, 0, len);
                          }
                          result = baos.toByteArray();
                      } catch (Exception e) {
                      } finally {
                          CloseUtils.closeSilently(is);
                          CloseUtils.closeSilently(baos);
                          if (conn != null) {
                              conn.disconnect();
                          }
                      }
                  }
                  return result;
              }
          
              public void postURLResponse(String urlString, HashMap<String, String> headers,
                                          byte[] postData, IWebCallback iWebCallback) {
                  if (urlString != null) {
                      HttpURLConnection conn = null; //連接對(duì)象
                      InputStream is = null;
                      ByteArrayOutputStream baos = null;
                      try {
                          URL url = new URL(urlString); //URL對(duì)象
                          if (urlString.startsWith(HTTPS)) {
                              conn = (HttpsURLConnection) url.openConnection();
                          } else {
                              conn = (HttpURLConnection) url.openConnection();
                          }
                          conn.setConnectTimeout(5 * 1000);
                          conn.setDoOutput(true);
                          conn.setRequestMethod(POST); //使用post請(qǐng)求
                          conn.setRequestProperty("Charsert", "UTF-8");
                          if (headers != null) {
                              for (Map.Entry<String, String> temp : headers.entrySet()) {
                                  conn.setRequestProperty(temp.getKey(), temp.getValue());
                              }
                          }
                          conn.getOutputStream().write(postData);
                          is = conn.getInputStream();   //獲取輸入流,此時(shí)才真正建立鏈接
                          baos = new ByteArrayOutputStream();
                          byte[] temp = new byte[1024];
                          int len;
                          while ((len = is.read(temp)) != -1) {
                              baos.write(temp, 0, len);
                          }
                          if (iWebCallback != null) {
                              iWebCallback.onCallback(conn.getResponseCode(), conn.getResponseMessage(), conn.getHeaderFields(), baos.toByteArray());
                          }
                      } catch (Exception e) {
                          int code = 600;
                          try {
                              code = conn == null ? 600 : conn.getResponseCode();
                          } catch (Exception e1) {
                          }
                          if (iWebCallback != null) {
                              iWebCallback.onFail(code, e.toString());
                          }
                      } finally {
                          CloseUtils.closeSilently(is);
                          CloseUtils.closeSilently(baos);
                          if (conn != null) {
                              conn.disconnect();
                          }
                      }
                  }
              }
          }
        
      • 用于解析支付訂單查詢返回信息解析

              import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
              import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
              
              @JacksonXmlRootElement(localName = "xml")
              public class WXPayResponse {
              
                  @JacksonXmlProperty(localName = "return_code")
                  private String returnCode;//返回碼
                  @JacksonXmlProperty(localName = "return_msg")
                  private String returnMsg;//返回信息
                  @JacksonXmlProperty(localName = "appid")
                  private String appid;//appid
                  @JacksonXmlProperty(localName = "mch_id")
                  private String mchId;//商戶號(hào)
                  @JacksonXmlProperty(localName = "nonce_str")
                  private String nonceStr;//隨機(jī)字符串
                  @JacksonXmlProperty(localName = "sign")
                  private String sign;//簽名
                  @JacksonXmlProperty(localName = "result_code")
                  private String resultCode;//業(yè)務(wù)結(jié)果
                  @JacksonXmlProperty(localName = "err_code")
                  private String errCode;//錯(cuò)誤代碼
                  @JacksonXmlProperty(localName = "err_code_des")
                  private String errCodeDes;//錯(cuò)誤代碼描述
                  @JacksonXmlProperty(localName = "device_info")
                  private String deviceInfo;//設(shè)備號(hào)
                  @JacksonXmlProperty(localName = "openid")
                  private String openid;//用戶標(biāo)識(shí)
                  @JacksonXmlProperty(localName = "is_subscribe")
                  private String isSubscribe;//是否關(guān)注公眾賬號(hào)
                  @JacksonXmlProperty(localName = "trade_type")
                  private String tradeType;//交易類型
                  @JacksonXmlProperty(localName = "trade_state")
                  private String tradeState;//交易狀態(tài)
                  @JacksonXmlProperty(localName = "bank_type")
                  private String bankType;//付款銀行
                  @JacksonXmlProperty(localName = "total_fee")
                  private String totalFee;//總金額
                  @JacksonXmlProperty(localName = "fee_type")
                  private String feeType;//貨幣種類
                  @JacksonXmlProperty(localName = "cash_fee")
                  private String cashFee;//現(xiàn)金支付金額
                  @JacksonXmlProperty(localName = "cash_fee_type")
                  private String cashFeeType;//現(xiàn)金支付貨幣類型
                  @JacksonXmlProperty(localName = "settlement_total_fee")
                  private String settlementTotalFee;//應(yīng)結(jié)訂單金額
                  @JacksonXmlProperty(localName = "coupon_fee")
                  private String couponFee;//代金券金額
                  @JacksonXmlProperty(localName = "coupon_count")
                  private String couponCount;//代金券使用數(shù)量
                  @JacksonXmlProperty(localName = "transaction_id")
                  private String transactionId;//微信支付訂單號(hào)
                  @JacksonXmlProperty(localName = "out_trade_no")
                  private String outTradeNo;//商戶訂單號(hào)
                  @JacksonXmlProperty(localName = "attach")
                  private String attach;//附加數(shù)據(jù)
                  @JacksonXmlProperty(localName = "time_end")
                  private String timeEnd;//支付完成時(shí)間
                  @JacksonXmlProperty(localName = "trade_state_desc")
                  private String tradeStateDesc;//交易狀態(tài)描述
              
                  public String getReturnCode() {
                      return returnCode;
                  }
              
                  public void setReturnCode(String returnCode) {
                      this.returnCode = returnCode;
                  }
              
                  public String getReturnMsg() {
                      return returnMsg;
                  }
              
                  public void setReturnMsg(String returnMsg) {
                      this.returnMsg = returnMsg;
                  }
              
                  public String getAppid() {
                      return appid;
                  }
              
                  public void setAppid(String appid) {
                      this.appid = appid;
                  }
              
                  public String getMchId() {
                      return mchId;
                  }
              
                  public void setMchId(String mchId) {
                      this.mchId = mchId;
                  }
              
                  public String getNonceStr() {
                      return nonceStr;
                  }
              
                  public void setNonceStr(String nonceStr) {
                      this.nonceStr = nonceStr;
                  }
              
                  public String getSign() {
                      return sign;
                  }
              
                  public void setSign(String sign) {
                      this.sign = sign;
                  }
              
                  public String getResultCode() {
                      return resultCode;
                  }
              
                  public void setResultCode(String resultCode) {
                      this.resultCode = resultCode;
                  }
              
                  public String getErrCode() {
                      return errCode;
                  }
              
                  public void setErrCode(String errCode) {
                      this.errCode = errCode;
                  }
              
                  public String getErrCodeDes() {
                      return errCodeDes;
                  }
              
                  public void setErrCodeDes(String errCodeDes) {
                      this.errCodeDes = errCodeDes;
                  }
              
                  public String getDeviceInfo() {
                      return deviceInfo;
                  }
              
                  public void setDeviceInfo(String deviceInfo) {
                      this.deviceInfo = deviceInfo;
                  }
              
                  public String getOpenid() {
                      return openid;
                  }
              
                  public void setOpenid(String openid) {
                      this.openid = openid;
                  }
              
                  public String getIsSubscribe() {
                      return isSubscribe;
                  }
              
                  public void setIsSubscribe(String isSubscribe) {
                      this.isSubscribe = isSubscribe;
                  }
              
                  public String getTradeType() {
                      return tradeType;
                  }
              
                  public void setTradeType(String tradeType) {
                      this.tradeType = tradeType;
                  }
              
                  public String getTradeState() {
                      return tradeState;
                  }
              
                  public void setTradeState(String tradeState) {
                      this.tradeState = tradeState;
                  }
              
                  public String getBankType() {
                      return bankType;
                  }
              
                  public void setBankType(String bankType) {
                      this.bankType = bankType;
                  }
              
                  public String getTotalFee() {
                      return totalFee;
                  }
              
                  public void setTotalFee(String totalFee) {
                      this.totalFee = totalFee;
                  }
              
                  public String getFeeType() {
                      return feeType;
                  }
              
                  public void setFeeType(String feeType) {
                      this.feeType = feeType;
                  }
              
                  public String getCashFee() {
                      return cashFee;
                  }
              
                  public void setCashFee(String cashFee) {
                      this.cashFee = cashFee;
                  }
              
                  public String getCashFeeType() {
                      return cashFeeType;
                  }
              
                  public void setCashFeeType(String cashFeeType) {
                      this.cashFeeType = cashFeeType;
                  }
              
                  public String getSettlementTotalFee() {
                      return settlementTotalFee;
                  }
              
                  public void setSettlementTotalFee(String settlementTotalFee) {
                      this.settlementTotalFee = settlementTotalFee;
                  }
              
                  public String getCouponFee() {
                      return couponFee;
                  }
              
                  public void setCouponFee(String couponFee) {
                      this.couponFee = couponFee;
                  }
              
                  public String getCouponCount() {
                      return couponCount;
                  }
              
                  public void setCouponCount(String couponCount) {
                      this.couponCount = couponCount;
                  }
              
                  public String getTransactionId() {
                      return transactionId;
                  }
              
                  public void setTransactionId(String transactionId) {
                      this.transactionId = transactionId;
                  }
              
                  public String getOutTradeNo() {
                      return outTradeNo;
                  }
              
                  public void setOutTradeNo(String outTradeNo) {
                      this.outTradeNo = outTradeNo;
                  }
              
                  public String getAttach() {
                      return attach;
                  }
              
                  public void setAttach(String attach) {
                      this.attach = attach;
                  }
              
                  public String getTimeEnd() {
                      return timeEnd;
                  }
              
                  public void setTimeEnd(String timeEnd) {
                      this.timeEnd = timeEnd;
                  }
              
                  public String getTradeStateDesc() {
                      return tradeStateDesc;
                  }
              
                  public void setTradeStateDesc(String tradeStateDesc) {
                      this.tradeStateDesc = tradeStateDesc;
                  }
              
                  @Override
                  public String toString() {
                      return "WXPayResponse{" +
                              "returnCode='" + returnCode + '\'' +
                              ", returnMsg='" + returnMsg + '\'' +
                              ", appid='" + appid + '\'' +
                              ", mchId='" + mchId + '\'' +
                              ", nonceStr='" + nonceStr + '\'' +
                              ", sign='" + sign + '\'' +
                              ", resultCode='" + resultCode + '\'' +
                              ", errCode='" + errCode + '\'' +
                              ", errCodeDes='" + errCodeDes + '\'' +
                              ", deviceInfo='" + deviceInfo + '\'' +
                              ", openid='" + openid + '\'' +
                              ", isSubscribe='" + isSubscribe + '\'' +
                              ", tradeType='" + tradeType + '\'' +
                              ", tradeState='" + tradeState + '\'' +
                              ", bankType='" + bankType + '\'' +
                              ", totalFee='" + totalFee + '\'' +
                              ", feeType='" + feeType + '\'' +
                              ", cashFee='" + cashFee + '\'' +
                              ", cashFeeType='" + cashFeeType + '\'' +
                              ", settlementTotalFee='" + settlementTotalFee + '\'' +
                              ", couponFee='" + couponFee + '\'' +
                              ", couponCount='" + couponCount + '\'' +
                              ", transactionId='" + transactionId + '\'' +
                              ", outTradeNo='" + outTradeNo + '\'' +
                              ", attach='" + attach + '\'' +
                              ", timeEnd='" + timeEnd + '\'' +
                              ", tradeStateDesc='" + tradeStateDesc + '\'' +
                              '}';
                  }
              }
        
      • 解析微信登錄支付簽名返回對(duì)象

              import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
              import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
              
              @JacksonXmlRootElement(localName = "xml")
              public class WXResponse {
              
                  @JacksonXmlProperty(localName = "return_code")
                  private String returnCode;//返回碼
                  @JacksonXmlProperty(localName = "return_msg")
                  private String returnMsg;//返回信息
                  @JacksonXmlProperty(localName = "appid")
                  private String appid;//appid
                  @JacksonXmlProperty(localName = "mch_id")
                  private String mchId;//商戶號(hào)
                  @JacksonXmlProperty(localName = "nonce_str")
                  private String nonceStr;//隨機(jī)字符串
                  @JacksonXmlProperty(localName = "sign")
                  private String sign;//簽名
                  @JacksonXmlProperty(localName = "result_code")
                  private String resultCode;//結(jié)果碼
                  @JacksonXmlProperty(localName = "err_code")
                  private String errCode;//錯(cuò)誤碼
                  @JacksonXmlProperty(localName = "err_code_des")
                  private String errCodeDes;//錯(cuò)誤描述
                  @JacksonXmlProperty(localName = "prepay_id")
                  private String prepayId;//支付id
                  @JacksonXmlProperty(localName = "trade_type")
                  private String tradeType;//支付類型
                  @JacksonXmlProperty(localName = "device_info")
                  private String deviceInfo;//設(shè)備信息
              
                  public String getReturnCode() {
                      return returnCode;
                  }
              
                  public void setReturnCode(String returnCode) {
                      this.returnCode = returnCode;
                  }
              
                  public String getReturnMsg() {
                      return returnMsg;
                  }
              
                  public void setReturnMsg(String returnMsg) {
                      this.returnMsg = returnMsg;
                  }
              
                  public String getAppid() {
                      return appid;
                  }
              
                  public void setAppid(String appid) {
                      this.appid = appid;
                  }
              
                  public String getMchId() {
                      return mchId;
                  }
              
                  public void setMchId(String mchId) {
                      this.mchId = mchId;
                  }
              
                  public String getNonceStr() {
                      return nonceStr;
                  }
              
                  public void setNonceStr(String nonceStr) {
                      this.nonceStr = nonceStr;
                  }
              
                  public String getSign() {
                      return sign;
                  }
              
                  public void setSign(String sign) {
                      this.sign = sign;
                  }
              
                  public String getResultCode() {
                      return resultCode;
                  }
              
                  public void setResultCode(String resultCode) {
                      this.resultCode = resultCode;
                  }
              
                  public String getErrCode() {
                      return errCode;
                  }
              
                  public void setErrCode(String errCode) {
                      this.errCode = errCode;
                  }
              
                  public String getErrCodeDes() {
                      return errCodeDes;
                  }
              
                  public void setErrCodeDes(String errCodeDes) {
                      this.errCodeDes = errCodeDes;
                  }
              
                  public String getPrepayId() {
                      return prepayId;
                  }
              
                  public void setPrepayId(String prepayId) {
                      this.prepayId = prepayId;
                  }
              
                  public String getTradeType() {
                      return tradeType;
                  }
              
                  public void setTradeType(String tradeType) {
                      this.tradeType = tradeType;
                  }
              
                  public String getDeviceInfo() {
                      return deviceInfo;
                  }
              
                  public void setDeviceInfo(String deviceInfo) {
                      this.deviceInfo = deviceInfo;
                  }
              
                  @Override
                  public String toString() {
                      return "WXResponse{" +
                              "returnCode='" + returnCode + '\'' +
                              ", returnMsg='" + returnMsg + '\'' +
                              ", appid='" + appid + '\'' +
                              ", mchId='" + mchId + '\'' +
                              ", nonceStr='" + nonceStr + '\'' +
                              ", sign='" + sign + '\'' +
                              ", resultCode='" + resultCode + '\'' +
                              ", errCode='" + errCode + '\'' +
                              ", errCodeDes='" + errCodeDes + '\'' +
                              ", prepayId='" + prepayId + '\'' +
                              ", tradeType='" + tradeType + '\'' +
                              ", deviceInfo='" + deviceInfo + '\'' +
                              '}';
                  }
              }
        
      • 用于返回給客戶端

              public class ResponseWX {
              
                  private String appid;
                  private String partnerid;
                  private String noncestr;
                  private String packageName;
                  private String prepayid;
                  private String timestamp;
                  private String sign;
                  private String orderNumber;
              
                  public String getAppid() {
                      return appid;
                  }
              
                  public void setAppid(String appid) {
                      this.appid = appid;
                  }
              
                  public String getPartnerid() {
                      return partnerid;
                  }
              
                  public void setPartnerid(String partnerid) {
                      this.partnerid = partnerid;
                  }
              
                  public String getNoncestr() {
                      return noncestr;
                  }
              
                  public void setNoncestr(String noncestr) {
                      this.noncestr = noncestr;
                  }
              
                  public String getPackageName() {
                      return packageName;
                  }
              
                  public void setPackageName(String packageName) {
                      this.packageName = packageName;
                  }
              
                  public String getPrepayid() {
                      return prepayid;
                  }
              
                  public void setPrepayid(String prepayid) {
                      this.prepayid = prepayid;
                  }
              
                  public String getTimestamp() {
                      return timestamp;
                  }
              
                  public void setTimestamp(String timestamp) {
                      this.timestamp = timestamp;
                  }
              
                  public String getSign() {
                      return sign;
                  }
              
                  public void setSign(String sign) {
                      this.sign = sign;
                  }
              
                  public String getOrderNumber() {
                      return orderNumber;
                  }
              
                  public void setOrderNumber(String orderNumber) {
                      this.orderNumber = orderNumber;
                  }
              
                  @Override
                  public String toString() {
                      return "ResponseWX{" +
                              "appid='" + appid + '\'' +
                              ", partnerid='" + partnerid + '\'' +
                              ", noncestr='" + noncestr + '\'' +
                              ", packageName='" + packageName + '\'' +
                              ", prepayid='" + prepayid + '\'' +
                              ", timestamp='" + timestamp + '\'' +
                              ", sign='" + sign + '\'' +
                              ", orderNumber='" + orderNumber + '\'' +
                              '}';
                  }
              }
        
      • 關(guān)鍵工具類

            import com.alibaba.fastjson.JSON;
            
            import java.util.*;
            
            public class WechartUtils {
            
                private static final String APP_ID = "";//應(yīng)用ID
                private static final String CT_NUMBER = "";//商戶號(hào)
                private static final String SECRET = "";//這些都能從官方文檔獲取
                private static final String KEY = "";//
                private static final String GET_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                        + APP_ID + "&secret=" + SECRET + "&code=%s&grant_type=authorization_code";
                private static final String GET_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s";
                private static final String GET_FIRST_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
                private static final String GET_ORDER = "https://api.mch.weixin.qq.com/pay/orderquery";
            
                public static boolean checkWXOrder(String orderNumber) {
                    SortedMap<String, String> temp = new TreeMap<String, String>() {
                        {
                            put("appid", APP_ID);//應(yīng)用ID
                            put("mch_id", CT_NUMBER);//商戶號(hào)
                            put("nonce_str", MD5Utils.MD5(String.valueOf(System.currentTimeMillis()), false));//隨機(jī)字符串
                            put("out_trade_no", orderNumber);//商戶訂單號(hào)
                        }
                    };
                    temp.put("sign", getSign(temp));
                    String xml = getXml(temp);
                    byte[] result = HttpUtils.getInstance().postURLResponse(GET_ORDER, null, xml.getBytes());
                    WXPayResponse wxPayResponse = XMLUtils.getObject(new String(result), WXPayResponse.class);
                    return wxPayResponse != null && "SUCCESS".equals(wxPayResponse.getReturnCode())
                            && "SUCCESS".equals(wxPayResponse.getResultCode())
                            && "SUCCESS".equals(wxPayResponse.getTradeState());
                }
            
                public static ResponseWX getWXResponse(String goodsName, String goodsDesc
                        , String orderNumber, String price, String ip) throws Exception {
                    SortedMap<String, String> temp = new TreeMap<String, String>() {
                        {
                            put("appid", APP_ID);//應(yīng)用ID
                            put("mch_id", CT_NUMBER);//商戶號(hào)
                            put("nonce_str", MD5Utils.MD5(String.valueOf(System.currentTimeMillis()), false));//隨機(jī)字符串
                            put("body", goodsName);//商品描述
                            put("attach", goodsDesc);//附加數(shù)據(jù)
                            put("out_trade_no", orderNumber);//商戶訂單號(hào)
                            put("total_fee", price);//總金額
                            put("spbill_create_ip", ip);//終端IP
                            put("notify_url", "");//這里填寫(xiě)自己的回調(diào)通知地址
                            put("trade_type", "APP");//交易類型
                        }
                    };
                    temp.put("sign", getSign(temp));
                    String xml = getXml(temp);
                    byte[] result = HttpUtils.getInstance().postURLResponse(GET_FIRST_ORDER, null, xml.getBytes());
                    WXResponse response = XMLUtils.getObject(new String(result), WXResponse.class);
                    if (response != null && "SUCCESS".equals(response.getReturnCode())
                            && "SUCCESS".equals(response.getResultCode())
                            && response.getPrepayId() != null) {
                        ResponseWX responseWX = new ResponseWX();
                        responseWX.setAppid(response.getAppid());
                        responseWX.setNoncestr(response.getNonceStr());
                        responseWX.setPackageName("Sign=WXPay");
                        responseWX.setPartnerid(response.getMchId());
                        responseWX.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000));
                        responseWX.setPrepayid(response.getPrepayId());
                        responseWX.setSign(getSign(new TreeMap<String, String>() {
                            {
                                put("appid", responseWX.getAppid());//應(yīng)用ID
                                put("partnerid", responseWX.getPartnerid());//商戶號(hào)
                                put("noncestr", responseWX.getNoncestr());//商品描述
                                put("package", responseWX.getPackageName());//隨機(jī)字符串
                                put("prepayid", responseWX.getPrepayid());//商戶訂單號(hào)
                                put("timestamp", responseWX.getTimestamp());//附加數(shù)據(jù)
                            }
                        }));
                        responseWX.setOrderNumber(orderNumber);
                        return responseWX;
                    }
                    throw new Exception("簽名失敗");
                }
            
                private static String getXml(SortedMap<String, String> data) {
                    StringBuilder result = new StringBuilder("<xml>");
                    for (Map.Entry<String, String> kv : data.entrySet()) {
                        result.append("<").append(kv.getKey()).append(">").append(kv.getValue())
                                .append("</").append(kv.getKey()).append(">");
                    }
                    return result.append("</xml>").toString();
                }
            
                private static String getSign(SortedMap<String, String> data) {
                    String result = null;
                    if (data != null) {
                        StringBuilder key = new StringBuilder();
                        boolean isFirst = true;
                        for (Map.Entry<String, String> kv : data.entrySet()) {
                            if (isFirst) {
                                isFirst = false;
                            } else {
                                key.append("&");
                            }
                            key.append(kv.getKey());
                            key.append("=");
                            key.append(kv.getValue());
                        }
                        key.append("&key=").append(KEY);
                        return MD5Utils.MD5(key.toString(), false).toUpperCase();
                    }
                    return result;
                }
            
                public static WechartUserResponse getUser(String authCode) {
                    WechartUserResponse result = null;
                    String host = String.format(GET_ACCESS_TOKEN, authCode);
                    byte[] response = HttpUtils.getInstance().getURLResponse(host, null);
                    if (response != null) {
                        AccessTokenResponse accessTokenResponse;
                        try {
                            accessTokenResponse = JSON.parseObject(new String(response), AccessTokenResponse.class);
                        } catch (Exception e) {
                            System.out.println("Format response error.e=" + e + ";response=" + new String(response));
                            return result;
                        }
                        if (accessTokenResponse != null && accessTokenResponse.accessToken != null) {
                            response = HttpUtils.getInstance().getURLResponse(host, null);
                            if (response != null) {
                                host = String.format(GET_USER_INFO, accessTokenResponse.accessToken, accessTokenResponse.openid);
                                response = HttpUtils.getInstance().getURLResponse(host, null);
                                try {
                                    result = JSON.parseObject(new String(response), WechartUserResponse.class);
                                    if (result.unionid == null) {
                                        System.out.println("Get user info error." + ";response=" + new String(response));
                                        result = null;
                                    }
                                } catch (Exception e) {
                                    System.out.println("Format response error.e=" + e + ";response=" + new String(response));
                                }
                            }
                        }
                    }
                    return result;
                }
            
                public static class WechartUserResponse {
                    private String openid;
                    private String nickname;
                    private int sex;
                    private String language;
                    private String city;
                    private String province;
                    private String country;
                    private String headimgurl;
                    private String unionid;
            
                    public String getOpenid() {
                        return openid;
                    }
            
                    public void setOpenid(String openid) {
                        this.openid = openid;
                    }
            
                    public String getNickname() {
                        return nickname;
                    }
            
                    public void setNickname(String nickname) {
                        this.nickname = nickname;
                    }
            
                    public int getSex() {
                        return sex;
                    }
            
                    public void setSex(int sex) {
                        this.sex = sex;
                    }
            
                    public String getLanguage() {
                        return language;
                    }
            
                    public void setLanguage(String language) {
                        this.language = language;
                    }
            
                    public String getCity() {
                        return city;
                    }
            
                    public void setCity(String city) {
                        this.city = city;
                    }
            
                    public String getProvince() {
                        return province;
                    }
            
                    public void setProvince(String province) {
                        this.province = province;
                    }
            
                    public String getCountry() {
                        return country;
                    }
            
                    public void setCountry(String country) {
                        this.country = country;
                    }
            
                    public String getHeadimgurl() {
                        return headimgurl;
                    }
            
                    public void setHeadimgurl(String headimgurl) {
                        this.headimgurl = headimgurl;
                    }
            
                    public String getUnionid() {
                        return unionid;
                    }
            
                    public void setUnionid(String unionid) {
                        this.unionid = unionid;
                    }
            
                    @Override
                    public String toString() {
                        return "WechartUserResponse{" +
                                "openid='" + openid + '\'' +
                                ", nickname='" + nickname + '\'' +
                                ", sex=" + sex +
                                ", language='" + language + '\'' +
                                ", city='" + city + '\'' +
                                ", province='" + province + '\'' +
                                ", country='" + country + '\'' +
                                ", headimgurl='" + headimgurl + '\'' +
                                ", unionid='" + unionid + '\'' +
                                '}';
                    }
                }
            
                private static class AccessTokenResponse {
                    private String accessToken;
                    private int expiresIn;
                    private String refreshToken;
                    private String openid;
                    private String scope;
                    private String unionid;
            
                    public String getAccessToken() {
                        return accessToken;
                    }
            
                    public void setAccessToken(String accessToken) {
                        this.accessToken = accessToken;
                    }
            
                    public int getExpiresIn() {
                        return expiresIn;
                    }
            
                    public void setExpiresIn(int expiresIn) {
                        this.expiresIn = expiresIn;
                    }
            
                    public String getRefreshToken() {
                        return refreshToken;
                    }
            
                    public void setRefreshToken(String refreshToken) {
                        this.refreshToken = refreshToken;
                    }
            
                    public String getOpenid() {
                        return openid;
                    }
            
                    public void setOpenid(String openid) {
                        this.openid = openid;
                    }
            
                    public String getScope() {
                        return scope;
                    }
            
                    public void setScope(String scope) {
                        this.scope = scope;
                    }
            
                    public String getUnionid() {
                        return unionid;
                    }
            
                    public void setUnionid(String unionid) {
                        this.unionid = unionid;
                    }
                }
            
            }
  • 因?yàn)橹虚g用了xml解析县习,所以需要導(dǎo)入"com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.5" 的jar包,這里為gradle導(dǎo)入

  • 最后服務(wù)器端只需要開(kāi)接口接收客戶端傳遞的code谆趾,然后調(diào)用getUser()方法返回用戶對(duì)象躁愿。

  • 至此微信登錄對(duì)接就完成了,如果還有其他問(wèn)題或者覺(jué)得不對(duì)的地方可以評(píng)論提出沪蓬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末彤钟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子跷叉,更是在濱河造成了極大的恐慌逸雹,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件云挟,死亡現(xiàn)場(chǎng)離奇詭異梆砸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)园欣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)帖世,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沸枯,你說(shuō)我怎么就攤上這事日矫。” “怎么了绑榴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵哪轿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我翔怎,道長(zhǎng)窃诉,這世上最難降的妖魔是什么杨耙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮褐奴,結(jié)果婚禮上按脚,老公的妹妹穿的比我還像新娘。我一直安慰自己敦冬,他們只是感情好辅搬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著脖旱,像睡著了一般堪遂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上萌庆,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天溶褪,我揣著相機(jī)與錄音,去河邊找鬼践险。 笑死猿妈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巍虫。 我是一名探鬼主播彭则,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼占遥!你這毒婦竟也來(lái)了俯抖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瓦胎,失蹤者是張志新(化名)和其女友劉穎芬萍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體搔啊,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柬祠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坯癣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓶盛。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖示罗,靈堂內(nèi)的尸體忽然破棺而出惩猫,到底是詐尸還是另有隱情,我是刑警寧澤蚜点,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布轧房,位于F島的核電站,受9級(jí)特大地震影響绍绘,放射性物質(zhì)發(fā)生泄漏奶镶。R本人自食惡果不足惜迟赃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厂镇。 院中可真熱鬧纤壁,春花似錦、人聲如沸捺信。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迄靠。三九已至秒咨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掌挚,已是汗流浹背雨席。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吠式,地道東北人陡厘。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像特占,于是被迫代替她去往敵國(guó)和親雏亚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容