iOS開發(fā) AIP支付總結(jié)

iOS開發(fā) IAP支付總結(jié)

一胸私、IAP介紹

1.1锌仅、簡介

這里先把官方文檔給大家

App 內(nèi)購買項目配置流程

內(nèi)購:只要在iPhone App上購買的不是實物產(chǎn)品(也就是虛擬產(chǎn)品如qq幣摄凡、魚翅蝎宇、電子書......) 都需要走內(nèi)購流程,蘋果這里面抽走30%

蘋果規(guī)定徽龟,凡是在App內(nèi)提供的服務(wù)需要付費時贤旷,必須使用IAP广料,比如說游戲的金幣,道具等幼驶;而在App外提供的服務(wù)需要付費時艾杏,可以使用其他的支付方式,比如支付寶SDK盅藻、微信SDK等购桑。說的更通俗一點,如果付費購買的商品是虛擬商品氏淑,比如游戲中的道具勃蜘,并不是現(xiàn)實中存在的,那么必須使用IAP假残;如果付費購買的商品是真實產(chǎn)品缭贡,比如在淘寶中買了件衣服炉擅,是實實在在存在的,那么沒有必要使用IAP阳惹。因此谍失,在使用IAP之前,首先要確認(rèn)是否一定要使用IAP莹汤,如果不使用IAP也可以快鱼,那么盡量不要用IAP,因為IAP流程纲岭、使用復(fù)雜度相比支付寶SDK抹竹、微信SDK來說,要復(fù)雜很多止潮。

1.2柒莉、內(nèi)購流程

1.1.1 填寫協(xié)議,稅務(wù)和銀行業(yè)務(wù)

1、登錄https://appstoreconnect.apple.com沽翔,選擇進(jìn)入App Store Connect兢孝。

2、進(jìn)入“協(xié)議仅偎、稅務(wù)和銀行業(yè)務(wù)”

3跨蟹、內(nèi)購用的是付費應(yīng)用程序,先簽署《付費應(yīng)用程序協(xié)議》橘沥,同意后狀態(tài)變更為“用戶信息待處理”窗轩,等待審核。

4座咆、狀態(tài)更改完畢后痢艺,點擊“開始設(shè)置稅務(wù)、銀行業(yè)務(wù)和聯(lián)系信息”介陶。
a.添加銀行賬戶,按照要求填寫相關(guān)內(nèi)容即可堤舒。


b.選擇報稅表,并填寫哺呜。(我是可愛的中國公民舌缤,在美國有沒有商業(yè)活動,所以我填的是否某残。)

然后繼續(xù)填寫報稅表国撵,按照填寫要求填寫就行了(要是英文閱讀有點困難,那就雙擊網(wǎng)頁玻墅,應(yīng)該會有翻譯成中文的功能介牙;沒有的話,那就詞典澳厢。环础。囚似。你懂得,哈哈哈)喳整, 我是個人開發(fā)者賬戶相對公司開發(fā)者賬戶填的會少一點,不過沒關(guān)系裸扶。都是一些基本信息框都。

c.填寫聯(lián)系信息,一共5個呵晨。高級管理魏保、財務(wù)、技術(shù)摸屠、法務(wù)谓罗、營銷。

5季二、上面的稅務(wù)表填完了之后檩咱,點擊“我的APP”,進(jìn)入到項目APP的信息頁胯舷,點擊功能刻蚯,在彈出的頁面點擊App內(nèi)購買項目后面的+。

創(chuàng)建完成之后 填寫內(nèi)購買項目信息

信息填寫完成了點擊右上角的 “存儲”桑嘶,然后點擊左邊 “App 內(nèi)購買項目”炊汹。出現(xiàn)“元數(shù)據(jù)丟失”說明里面信息沒填寫完整,在點進(jìn)去填寫逃顶。直到顯示“準(zhǔn)備提交”讨便。

6、添加沙箱測試人員

7以政、我們需要在工程里配置好證書霸褒,測試證書是必須的因為我們內(nèi)購需要連接到蘋果的App Store的,需要正式的測試證書才能測試盈蛮,同時把下圖工程中的這一配置打開

二傲霸、IAP代碼部分

我這里就直接上代碼記錄了

2.1、大體代碼流程

typedefenum: NSUInteger {

    EPaymentTransactionStateNoPaymentPermission,//沒有Payment權(quán)限

    EPaymentTransactionStateAddPaymentFailed,//addPayment失敗

    EPaymentTransactionStatePurchasing,//正在購買

    EPaymentTransactionStatePurchased,//購買完成(銷毀交易)

    EPaymentTransactionStateFailed,//購買失敗(銷毀交易)

    EPaymentTransactionStateCancel,//用戶取消

    EPaymentTransactionStateRestored,//恢復(fù)購買(銷毀交易)

    EPaymentTransactionStateDeferred,//最終狀態(tài)未確定

} EPaymentTransactionState;

// 這個大家要熟悉哦~

步驟一:App Store請求內(nèi)購項

注意:此步驟建議在開始創(chuàng)建購買訂單前完成眉反,這樣可以減少購買時查詢訂單的時間

1昙啄、判斷用戶是否具備支付權(quán)限

//是否允許內(nèi)購
if ([SKPaymentQueue canMakePayments]) {
    [self getRequestAppleProduct];
}else{
    [self removeLoadingHandle];
    [self removeIAPObserverHandle];
    dispatch_async(dispatch_get_main_queue(), ^{
        [MBProgressHUDManager showFailedHUD:TZKeyWindow text:@"請打開Apple支付"];
    });
}

2、創(chuàng)建一個商品查詢的請求寸五,productIdentifiers指需要查詢的“產(chǎn)品ID”的數(shù)組

- (void)getRequestAppleProduct
{
    NSLog(@"---------請求對應(yīng)的產(chǎn)品信息------------");
    [MBProgressHUDManager showHUD:TZKeyWindow text:@"等待響應(yīng)..."];
    NSArray *product = [[NSArray alloc] initWithObjects:self.productID, nil];
    NSSet *nsset = [NSSet setWithArray:product];
    
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    
    [request start];
    
}

查詢的結(jié)果將通過SKProductsRequestDelegate得到查詢的結(jié)果
獲取商品的查詢結(jié)果

#pragma mark - SKProductsRequestDelegate
//接收到產(chǎn)品的返回信息梳凛,然后用返回的商品信息進(jìn)行發(fā)起購買請求
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response NS_AVAILABLE_IOS(3_0){
    
    
    NSArray *product = response.products;
    //沒有產(chǎn)品
    if([product count] == 0){
        [self removeLoadingHandle];
        [self removeIAPObserverHandle];
        dispatch_async(dispatch_get_main_queue(), ^{
            [MBProgressHUDManager showFailedHUD:TZKeyWindow text:@"網(wǎng)絡(luò)開小差了,請稍后重試"];
        });
        return;
    }
    
    
    SKProduct *requestProduct = nil;
    for (SKProduct *pro in product) {
        
        CYLOG(@"描述信息-%@", [pro description]);
        CYLOG(@"產(chǎn)品標(biāo)題-%@", [pro localizedTitle]);
        CYLOG(@"產(chǎn)品描述信息-%@", [pro localizedDescription]);
        CYLOG(@"價格-%@", [pro price]);
        CYLOG(@"Product id-%@", [pro productIdentifier]);
        CYLOG(@"位置-%@", pro.priceLocale.localeIdentifier);
        
        
        // 確保訂單的正確性
        if([pro.productIdentifier isEqualToString:self.productID]){
            requestProduct = pro;
            SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:requestProduct];
            payment.applicationUsername = self.orderId;
            [[SKPaymentQueue defaultQueue] addPayment:payment];
            break;
        }
    }
}``

步驟二:開始構(gòu)建購買請求

SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];

步驟三:添加支付交易的Observer

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

注意在適當(dāng)?shù)臅r候移除Observer

[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

可以通過遵循SKPaymentTransactionObserver協(xié)議來監(jiān)聽整個交易的過程

交易狀態(tài)發(fā)生改變時,包括狀態(tài)的改變梳杏,交易的結(jié)束

//監(jiān)聽購買結(jié)果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
    
    NSLog(@"==監(jiān)聽購買結(jié)果==");
    
    [self addLoadingHandle];
    [self addIAPObserverHandle];
    
    for(SKPaymentTransaction *tran in transactions){
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:
            {
                NSLog(@"交易完成");
                [self didPurchaseTransaction:tran queue:queue];
            }
                
                break;
            case SKPaymentTransactionStatePurchasing:{
                dispatch_async(dispatch_get_main_queue(), ^{
                    [MBProgressHUDManager showHUD:TZKeyWindow text:@"正在購買..."];
                });
            }
                
                break;
            case SKPaymentTransactionStateRestored:{
                CYLOG(@"已經(jīng)購買過商品");
                //消耗型不用寫
//                                [self removeLoadingHandle];
//                                [self removeIAPObserverHandle];
//                                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            }
                break;
            case SKPaymentTransactionStateFailed:{
                NSLog(@"交易失敗");
                [self removeLoadingHandle];
                [self removeIAPObserverHandle];
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [MBProgressHUDManager showFailedHUD:TZKeyWindow text:@"交易失敗"];
                });
            }
                
                break;
            case SKPaymentTransactionStateDeferred:{
                NSLog(@"還在隊列里");
                dispatch_async(dispatch_get_main_queue(), ^{
                    [MBProgressHUDManager showHUD:TZKeyWindow text:@"正在購買..."];
                });
            }
                
                break;
            default:
                break;
        }
    }
}

步驟四:校驗憑證

我這里直接把我這邊相關(guān)的思路以及代碼提供大家參考了:
有問題可以隨時聯(lián)系我韧拒、我后面會講一下我所遇到過的坑以及解決方案淹接。

#pragma mark Transaction State

我這里使用后臺校驗憑證、更加安全
- (void)didPurchaseTransaction:(SKPaymentTransaction *)transaction queue:(SKPaymentQueue*)queue
{
    CYLOG(@"transaction purchased with product ---%@", transaction.payment.productIdentifier);
    CYLOG(@"transaction ID ---%@", transaction.transactionIdentifier);
    if(transaction.payment.productIdentifier != nil){
        if([self.orderId length] && !self.ischecking){
            //如果這個參數(shù)存在叛溢,則肯定是通過主動發(fā)起購買請求引起的
            //在支付成功后塑悼,將parameters中的預(yù)訂單號存起來,并與蘋果的訂單號綁定起來楷掉,并存儲到keychain中
            if([self.orderId length] && transaction.transactionIdentifier){
                [SAMKeychain setPassword:self.orderId forService:TZServiceKey account:transaction.transactionIdentifier];
            }
        }
    }
    WS(weakSelf);
    // appStoreReceiptURL iOS7.0增加的厢蒜,購買交易完成后,會將憑據(jù)存放在該地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買憑據(jù)
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    NSString *applicationUsername = transaction.payment.applicationUsername;
    NSString *productId = transaction.payment.productIdentifier;
    NSString *transactionId = transaction.transactionIdentifier;
    //發(fā)送POST請求烹植,對購買憑據(jù)進(jìn)行驗證
    //測試驗證地址:https://sandbox.itunes.apple.com/verifyReceipt
    //正式驗證地址:https://buy.itunes.apple.com/verifyReceipt
    if(applicationUsername.length == 0){
        NSString *savedOrderNumber = [SAMKeychain passwordForService:TZServiceKey account:transactionId];
        if ([savedOrderNumber length]) {
            applicationUsername = savedOrderNumber;//獲取到訂單號
        }
    }
    if ([applicationUsername length] && [encodeStr length] && [productId length] && [transactionId length]) {
        [[HTTPAPIManager manager] reqeustPayAppleReceiptWithOutTradeNo:applicationUsername receiptData:encodeStr useSandbox:TZPAYSandbox productId:productId transactionId:transactionId success:^(NSURLSessionDataTask * _Nullable task, id  _Nullable responseObject, NSDictionary * _Nullable inforDict) {
            _errTimes = 0;
            weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:(2.0)
                                                              target:weakSelf
                                                            selector:@selector(handleTimer:)
                                                            userInfo:@{@"transaction":transaction}
                                                             repeats:YES];
            [[NSRunLoop mainRunLoop]addTimer:weakSelf.timer forMode:NSDefaultRunLoopMode];
            [weakSelf.timer setFireDate:[NSDate date]];
        } failure:^(NSURLSessionDataTask * _Nullable task, YWHTTPError * _Nullable error, NSDictionary * _Nullable responseDict) {
            _errTimes = 0;
            weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:(2.0)
                                                              target:weakSelf
                                                            selector:@selector(handleTimer:)
                                                            userInfo:@{@"transaction":transaction}
                                                             repeats:YES];
            [[NSRunLoop mainRunLoop]addTimer:weakSelf.timer forMode:NSDefaultRunLoopMode];
            [weakSelf.timer setFireDate:[NSDate date]];
        }];
        if (!_ischecking) {
            [MBProgressHUDManager showHUD:TZKeyWindow text:@"正在確認(rèn)支付..."];
        }
    } else {
        [[HTTPAPIManager manager] reqeustAppleFailRecordWithOutTradeNo:applicationUsername receiptData:encodeStr productId:productId transactionId:transactionId success:^(NSURLSessionDataTask * _Nullable task, id  _Nullable responseObject, NSDictionary * _Nullable inforDict) {
            _ischecking = NO;
            [MBProgressHUDManager hiddenHUD:TZKeyWindow];
            [weakSelf destroyTimer];
            [weakSelf removeLoadingHandle];
            [weakSelf removeIAPObserverHandle];
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            [[NSNotificationCenter defaultCenter] postNotificationName:TZBuyVipSuccessNotification object:nil userInfo:nil];
        } failure:^(NSURLSessionDataTask * _Nullable task, YWHTTPError * _Nullable error, NSDictionary * _Nullable responseDict) {
            [MBProgressHUDManager hiddenHUD:TZKeyWindow];
        }];
    }
}

三斑鸦、重點總結(jié)

1.獲取內(nèi)購列表(從App內(nèi)讀取或從自己服務(wù)器讀取)

2.App Store請求可用的內(nèi)購列表

3.向用戶展示內(nèi)購列表

4.用戶選擇了內(nèi)購列表草雕,再發(fā)個購買請求巷屿,收到購買完成的回調(diào)(購買完成
后會把錢打給申請內(nèi)購的銀行卡內(nèi))

5.購買流程結(jié)束后, 向服務(wù)器發(fā)起驗證憑證以及支付結(jié)果的請求

6.自己的服務(wù)器將支付結(jié)果信息返回給前端并發(fā)放虛擬產(chǎn)品

7.服務(wù)端的工作比較簡單,分4步:

  7.1.接收ios端發(fā)過來的購買憑證墩虹。
  
  7.2.判斷憑證是否已經(jīng)存在或驗證過嘱巾,然后存儲該憑證。
  
  7.3.將該憑證發(fā)送到蘋果的服務(wù)器驗證诫钓,并將驗證結(jié)果返回給客戶端浓冒。
  
   7.4.如果需要,修改用戶相應(yīng)的會員權(quán)限尖坤。
   
   7.5.考慮到網(wǎng)絡(luò)異常情況稳懒,服務(wù)器的驗證應(yīng)該是一個可恢復(fù)的隊列,如果網(wǎng)絡(luò)失敗了慢味,應(yīng)該進(jìn)行重試场梆。
   
簡單來說就是將該購買憑證用Base64編碼,然后POST給蘋果的驗證服務(wù)
器纯路,蘋果將驗證結(jié)果以JSON形式返回或油。

四、總結(jié)坑

SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:requestProduct];
payment.applicationUsername = self.orderId;
[[SKPaymentQueue defaultQueue] addPayment:payment];
//我這里剛才只是對訂單號的存儲驰唬、并且把訂單號存在了applicationUsername 

上面的處理中顶岸、上線后我這里第一個測出了真實支付中的漏單情況、訂單號返回的nil,由于對payment.applicationUsername的極度信任叫编、造成自己不得不緊急解決下這個問題發(fā)版辖佣、不過我事先作的還是有一些功課的、做了埋點、及時的定位到了問題、并且讓后臺進(jìn)行了手動補單师枣。相信大家看到這里的時候就不會只是簡單的這樣做了。

五世蔗、解決方案

1端逼、針對掉單的問題、網(wǎng)上的資料討論的太多了我這里簡單說下我的方案吧

我這里進(jìn)行了對訂單號污淋、transactionId顶滩、進(jìn)行對應(yīng)的鑰匙串存儲。這樣可以解決大部分的異常場景寸爆、基本沒什么漏單了礁鲁、并且我對掉單每次啟動進(jìn)行了掉單查詢、還有就是再次購買頁面也會有相應(yīng)的提示而昨、讓用戶自己繼續(xù)處理救氯、便可以重新提交訂單了找田。方便太多啦

/// 掉單處理
- (void)checkIAPHandle{
    _ischecking = YES;
    _isShowErrorM = NO;
    [self addIAPObserverHandle];
}


- (void)checkTransactionHandle
{
    NSArray *transactions = [SKPaymentQueue defaultQueue].transactions;
    if (transactions && [transactions isKindOfClass:[NSArray class]] && [transactions count]) {
        for (SKPaymentTransaction *transaction in transactions) {
            if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
                 NSString *title = @"您有一筆會員訂單未完成歌憨,請繼續(xù)處理";
                           NYAlertView *alertView = [[NYAlertView alloc] initWithTitle:@"發(fā)現(xiàn)未完成訂單"
                                                                               message:title
                                                                     cancelButtonTitle:nil
                                                                     otherButtonTitles:@"繼續(xù)處理", nil];
                           [alertView setClickButtonBlock:^(NYAlertView * _Nonnull alert, NSInteger index) {
                               [[ApplePayManager sharedManager] handleCheckPurchaseTransaction:transaction];
                           }];
                           alertView.titleTextAlignment = NSTextAlignmentLeft;
                           alertView.messageTextAlignment = NSTextAlignmentLeft;
                           [alertView.otherButton setTitleColor:[UIColor wb_colorWithHexString:@"F44A4A"] forState:UIControlStateNormal];
                           [alertView show];
            }
        }
    }
}

六、附言

大家做內(nèi)購過程中遇到問題可以隨時溝通哈6昭谩N竦铡! QQ:304517331

有好的建議也記得及時分享哦??

祝大家工作順利F岣摹P牧濉!挫剑!~~~~

七去扣、干貨

https://developer.apple.com/documentation/storekit/in-app_purchase/validating_receipts_with_the_app_store?language=objc

59601586421668_.pic.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市樊破,隨后出現(xiàn)的幾起案子愉棱,更是在濱河造成了極大的恐慌,老刑警劉巖哲戚,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奔滑,死亡現(xiàn)場離奇詭異,居然都是意外死亡顺少,警方通過查閱死者的電腦和手機朋其,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脆炎,“玉大人梅猿,你說我怎么就攤上這事∶朐#” “怎么了粒没?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長簇爆。 經(jīng)常有香客問我癞松,道長爽撒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任响蓉,我火速辦了婚禮硕勿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枫甲。我一直安慰自己源武,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布想幻。 她就那樣靜靜地躺著粱栖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脏毯。 梳的紋絲不亂的頭發(fā)上闹究,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音食店,去河邊找鬼渣淤。 笑死,一個胖子當(dāng)著我的面吹牛吉嫩,可吹牛的內(nèi)容都是我干的价认。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼自娩,長吁一口氣:“原來是場噩夢啊……” “哼用踩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起忙迁,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脐彩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后动漾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丁屎,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年旱眯,在試婚紗的時候發(fā)現(xiàn)自己被綠了晨川。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡删豺,死狀恐怖共虑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呀页,我是刑警寧澤妈拌,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響尘分,放射性物質(zhì)發(fā)生泄漏猜惋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一培愁、第九天 我趴在偏房一處隱蔽的房頂上張望著摔。 院中可真熱鬧,春花似錦定续、人聲如沸谍咆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摹察。三九已至,卻和暖如春倡鲸,著一層夾襖步出監(jiān)牢的瞬間供嚎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工旦签, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留查坪,地道東北人寸宏。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓宁炫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親氮凝。 傳聞我的和親對象是個殘疾皇子羔巢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356