স্থানীয়ভাবে একটি সম্পূর্ণ সমাধান আইওএস 7 এ একটি অ্যাপ্লিকেশন প্রাপ্তি এবং বান্ডিলের প্রাপ্তিগুলিকে বৈধতা দেয়


160

আমি প্রচুর দস্তাবেজ এবং কোড পড়েছি যা তাত্ত্বিকভাবে কোনও অ্যাপ্লিকেশন এবং / অথবা বান্ডিলের রশিদকে বৈধতা দেবে।

এসএসএল, শংসাপত্রগুলি, এনক্রিপশন ইত্যাদির বিষয়ে আমার জ্ঞান প্রায় শূন্য, এই প্রতিশ্রুতিবদ্ধটির মতো আমি যে সমস্ত ব্যাখ্যা পড়েছি তা বুঝতে আমার অসুবিধা হয়েছে।

তারা বলছেন ব্যাখ্যাগুলি অসম্পূর্ণ কারণ প্রতিটি ব্যক্তিকে এটি কীভাবে করা যায় তা নির্ধারণ করতে হবে, বা হ্যাকারদের কাছে একটি ক্র্যাকার অ্যাপ্লিকেশন তৈরি করা সহজ কাজ থাকবে যা নিদর্শনগুলি সনাক্ত করতে এবং সনাক্ত করতে এবং অ্যাপ্লিকেশনটিকে প্যাচ করতে পারে। ঠিক আছে, আমি এটি একটি নির্দিষ্ট পয়েন্ট পর্যন্ত একমত। আমি মনে করি তারা কীভাবে এটি করবেন তা সম্পূর্ণ ব্যাখ্যা করতে পারেন এবং "এই পদ্ধতিটি সংশোধন করুন", "এই অন্যান্য পদ্ধতিটি সংশোধন করুন", "এই ভেরিয়েবলটিকে অস্পষ্ট করুন", "এই এবং এর নাম পরিবর্তন করুন" ইত্যাদি একটি সতর্কতা রেখেছিলেন।

আমি পাঁচ বছর বয়সে আইওএস-তে স্থানীয়ভাবে যাচাইকরণ, বান্ডিল প্রাপ্তি এবং অ্যাপ-অ্যাপ্লিকেশন কেনার রসিদগুলি কীভাবে স্পষ্টভাবে ব্যাখ্যা করতে পারি, সেখানে কিছু ভাল আত্মা কীভাবে ব্যাখ্যা করতে পারেন?

ধন্যবাদ !!!


আপনার অ্যাপ্লিকেশনগুলিতে যদি আপনার কোনও সংস্করণ কাজ করে এবং আপনার উদ্বেগগুলি হ্যাকাররা দেখতে পাবেন যে আপনি এটি কীভাবে করেছিলেন তবে কেবল এখানে প্রকাশের আগে আপনার সংবেদনশীল পদ্ধতিগুলি পরিবর্তন করুন। স্ট্রিংগুলি অবলম্বন করুন, লাইনের ক্রম পরিবর্তন করুন, আপনি যেভাবে লুপ করবেন তার পদ্ধতি পরিবর্তন করুন (গণনা এবং তদ্বিপরীতকে অবরুদ্ধ করার জন্য ব্যবহার থেকে শুরু করুন) এবং এর মতো জিনিসগুলি। স্পষ্টতই, এখানে পোস্ট করা হতে পারে এমন কোড ব্যবহার করা প্রত্যেক ব্যক্তিকে একই কাজ করতে হবে, সহজেই হ্যাক হওয়ার ঝুঁকি নেই।


1
যথাযথ সতর্কতা: স্থানীয়ভাবে এটি করা আপনার অ্যাপ্লিকেশনটির বাইরে এই ফাংশনটি প্যাচ করা অনেক সহজ কাজ hell
নিনজা লাইকস চিজ

2
ঠিক আছে, আমি জানি, তবে মূল বিষয়টি হ'ল কঠিন কাজ করা এবং স্বয়ংক্রিয় ক্র্যাকিং / প্যাচিং প্রতিরোধ করা। প্রশ্নটি হ'ল কোনও হ্যাকার যদি সত্যিই আপনার অ্যাপ্লিকেশনটিকে ক্র্যাক করতে চায় তবে সে স্থানীয় বা দূরবর্তী যে কোনও পদ্ধতি ব্যবহার করবে না it আবার স্বয়ংক্রিয় প্যাচিং প্রতিরোধের জন্য, ধারণাটি হ'ল আপনার প্রকাশিত প্রতিটি নতুন সংস্করণ সামান্য পরিবর্তন করা।
হাঁস

4
@ নিনজালাইকস চিজ - কোনও সার্ভারে যাচাইকরণ করা হলেও চেকটি নপ করতে পারে।
হাঁস

14
দুঃখিত, তবে এটি বাহানা নয়। লেখকের একটাই কাজটি হ'ল এই কোড হিসাবে কোডটি ব্যবহার করবেন না তা বলা। কোনও উদাহরণ ছাড়াই রকেট বিজ্ঞানী না হয়ে এটি বোঝা অসম্ভব।
হাঁসের

3
আপনি যদি ডিআরএম প্রয়োগের ক্ষেত্রে বিরক্ত না চান তবে স্থানীয় যাচাইকরণ নিয়ে বিরক্ত করবেন না। আপনার অ্যাপ্লিকেশন থেকে সরাসরি অ্যাপলের কাছে রসিদটি পোস্ট করুন এবং তারা সহজেই পার্স করা JSON ফর্ম্যাটে আপনাকে এটি আবার আপনার কাছে প্রেরণ করবে। জলদস্যুদের পক্ষে এটি ক্র্যাক করা তুচ্ছ, তবে আপনি যদি কেবল ফ্রিমিয়ামে স্থানান্তর করে থাকেন এবং জলদস্যুতা সম্পর্কে চিন্তা করেন না তবে এটি খুব সহজ কোডের কয়েকটি লাইন।
ডান ফাবুলিচ

উত্তর:


146

আমি আমার অ্যাপ্লিকেশন কেনার লাইব্রেরি আরএমএসটোরে এটি কীভাবে সমাধান করেছি তার একটি পদচারণা এখানে । আমি কীভাবে কোনও লেনদেন যাচাই করতে হবে তা ব্যাখ্যা করব, যার মধ্যে পুরো প্রাপ্তি যাচাই করা রয়েছে।

এক পলকে

রসিদটি পান এবং লেনদেন যাচাই করুন। যদি এটি ব্যর্থ হয়, রসিদটি রিফ্রেশ করুন এবং আবার চেষ্টা করুন। প্রাপ্তি রিফ্রেশ হিসাবে এটি যাচাইকরণ প্রক্রিয়াটিকে অবিচ্ছিন্ন করে তোলে makes

আরএমএসস্টোর অ্যাপ রিসিপটিভিয়ার থেকে :

RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
const BOOL verified = [self verifyTransaction:transaction inReceipt:receipt success:successBlock failure:nil]; // failureBlock is nil intentionally. See below.
if (verified) return;

// Apple recommends to refresh the receipt if validation fails on iOS
[[RMStore defaultStore] refreshReceiptOnSuccess:^{
    RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
    [self verifyTransaction:transaction inReceipt:receipt success:successBlock failure:failureBlock];
} failure:^(NSError *error) {
    [self failWithBlock:failureBlock error:error];
}];

প্রাপ্তির তথ্য প্রাপ্তি

প্রাপ্তিটি [[NSBundle mainBundle] appStoreReceiptURL]পিসিএসএস container ধারক হিসাবে রয়েছে। আমি ক্রিপ্টোগ্রাফিটি স্তন্যপান করি তাই আমি এই ধারকটি খোলার জন্য ওপেনএসএসএল ব্যবহার করেছি। অন্যরা দৃশ্যত এটি সিস্টেম ফ্রেমওয়ার্কের সাথে খাঁটিভাবে সম্পাদন করেছেন ।

আপনার প্রকল্পে ওপেনএসএসএল যুক্ত করা তুচ্ছ নয়। RMStore উইকি সাহায্য করা উচিত।

আপনি যদি পিকেসিএস 7 ধারকটি খোলার জন্য ওপেনএসএসএল ব্যবহারের বিকল্প বেছে নেন তবে আপনার কোডটি এর মতো দেখতে পারে। আরএমএপ্পি রিসিপট থেকে :

+ (NSData*)dataFromPKCS7Path:(NSString*)path
{
    const char *cpath = [[path stringByStandardizingPath] fileSystemRepresentation];
    FILE *fp = fopen(cpath, "rb");
    if (!fp) return nil;

    PKCS7 *p7 = d2i_PKCS7_fp(fp, NULL);
    fclose(fp);

    if (!p7) return nil;

    NSData *data;
    NSURL *certificateURL = [[NSBundle mainBundle] URLForResource:@"AppleIncRootCertificate" withExtension:@"cer"];
    NSData *certificateData = [NSData dataWithContentsOfURL:certificateURL];
    if ([self verifyPKCS7:p7 withCertificateData:certificateData])
    {
        struct pkcs7_st *contents = p7->d.sign->contents;
        if (PKCS7_type_is_data(contents))
        {
            ASN1_OCTET_STRING *octets = contents->d.data;
            data = [NSData dataWithBytes:octets->data length:octets->length];
        }
    }
    PKCS7_free(p7);
    return data;
}

আমরা পরে যাচাইয়ের বিশদটি নিয়ে যাব।

প্রাপ্তির ক্ষেত্রগুলি পাচ্ছেন

প্রাপ্তিটি ASN1 ফর্ম্যাটে প্রকাশ করা হয়। এটিতে সাধারণ তথ্য, যাচাইকরণের উদ্দেশ্যে কিছু ক্ষেত্র রয়েছে (আমরা পরে তা আসব) এবং অ্যাপ্লিকেশন ক্রয়ের প্রতিটি প্রযোজ্য নির্দিষ্ট তথ্য রয়েছে।

আবার, এএসএন 1 পড়ার ক্ষেত্রে ওপেনএসএসএল উদ্ধার করতে আসে। আরএমএএপআরসিপেট থেকে কয়েকটি সহায়ক পদ্ধতি ব্যবহার করে:

NSMutableArray *purchases = [NSMutableArray array];
[RMAppReceipt enumerateASN1Attributes:asn1Data.bytes length:asn1Data.length usingBlock:^(NSData *data, int type) {
    const uint8_t *s = data.bytes;
    const NSUInteger length = data.length;
    switch (type)
    {
        case RMAppReceiptASN1TypeBundleIdentifier:
            _bundleIdentifierData = data;
            _bundleIdentifier = RMASN1ReadUTF8String(&s, length);
            break;
        case RMAppReceiptASN1TypeAppVersion:
            _appVersion = RMASN1ReadUTF8String(&s, length);
            break;
        case RMAppReceiptASN1TypeOpaqueValue:
            _opaqueValue = data;
            break;
        case RMAppReceiptASN1TypeHash:
            _hash = data;
            break;
        case RMAppReceiptASN1TypeInAppPurchaseReceipt:
        {
            RMAppReceiptIAP *purchase = [[RMAppReceiptIAP alloc] initWithASN1Data:data];
            [purchases addObject:purchase];
            break;
        }
        case RMAppReceiptASN1TypeOriginalAppVersion:
            _originalAppVersion = RMASN1ReadUTF8String(&s, length);
            break;
        case RMAppReceiptASN1TypeExpirationDate:
        {
            NSString *string = RMASN1ReadIA5SString(&s, length);
            _expirationDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
    }
}];
_inAppPurchases = purchases;

অ্যাপ্লিকেশন ক্রয় করা

প্রতিটি অ্যাপ্লিকেশন ক্রয়ও ASN1 এ। এটিকে পার্স করা সাধারণ প্রাপ্তির তথ্য পার্স করার চেয়ে অনেক অনুরূপ।

একই সহায়ক সহায়ক পদ্ধতি ব্যবহার করে আরএমএপ্পেরসিপ্ট থেকে :

[RMAppReceipt enumerateASN1Attributes:asn1Data.bytes length:asn1Data.length usingBlock:^(NSData *data, int type) {
    const uint8_t *p = data.bytes;
    const NSUInteger length = data.length;
    switch (type)
    {
        case RMAppReceiptASN1TypeQuantity:
            _quantity = RMASN1ReadInteger(&p, length);
            break;
        case RMAppReceiptASN1TypeProductIdentifier:
            _productIdentifier = RMASN1ReadUTF8String(&p, length);
            break;
        case RMAppReceiptASN1TypeTransactionIdentifier:
            _transactionIdentifier = RMASN1ReadUTF8String(&p, length);
            break;
        case RMAppReceiptASN1TypePurchaseDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _purchaseDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
        case RMAppReceiptASN1TypeOriginalTransactionIdentifier:
            _originalTransactionIdentifier = RMASN1ReadUTF8String(&p, length);
            break;
        case RMAppReceiptASN1TypeOriginalPurchaseDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _originalPurchaseDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
        case RMAppReceiptASN1TypeSubscriptionExpirationDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _subscriptionExpirationDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
        case RMAppReceiptASN1TypeWebOrderLineItemID:
            _webOrderLineItemID = RMASN1ReadInteger(&p, length);
            break;
        case RMAppReceiptASN1TypeCancellationDate:
        {
            NSString *string = RMASN1ReadIA5SString(&p, length);
            _cancellationDate = [RMAppReceipt formatRFC3339String:string];
            break;
        }
    }
}]; 

এটি লক্ষ করা উচিত যে নির্দিষ্ট অ্যাপ্লিকেশন ক্রয়গুলি যেমন ভোক্তাযোগ্য এবং অ-পুনর্নবীকরণযোগ্য সাবস্ক্রিপশন, প্রাপ্তি একবারে উপস্থিত হবে। ক্রয়ের পরে আপনার এই অধিকারগুলি যাচাই করা উচিত (আবার, আরএমএসটিওর এটিতে আপনাকে সহায়তা করে)।

এক নজরে যাচাইকরণ

এখন আমরা প্রাপ্তি এবং এর অ্যাপ্লিকেশনগুলির সমস্ত ক্রয় থেকে সমস্ত ক্ষেত্র পেয়েছি। প্রথমে আমরা রসিদটি নিজেই যাচাই করি এবং তারপরে আমরা সহজভাবে যাচাই করেছিলাম যে রশিদে লেনদেনের পণ্য রয়েছে।

নীচে সেই পদ্ধতিটি যা আমরা শুরুতে ফিরে ডাকলাম। আরএমএসটোর অ্যাপ রিসিপটিভেরিফিকটর থেকে :

- (BOOL)verifyTransaction:(SKPaymentTransaction*)transaction
                inReceipt:(RMAppReceipt*)receipt
                           success:(void (^)())successBlock
                           failure:(void (^)(NSError *error))failureBlock
{
    const BOOL receiptVerified = [self verifyAppReceipt:receipt];
    if (!receiptVerified)
    {
        [self failWithBlock:failureBlock message:NSLocalizedString(@"The app receipt failed verification", @"")];
        return NO;
    }
    SKPayment *payment = transaction.payment;
    const BOOL transactionVerified = [receipt containsInAppPurchaseOfProductIdentifier:payment.productIdentifier];
    if (!transactionVerified)
    {
        [self failWithBlock:failureBlock message:NSLocalizedString(@"The app receipt doest not contain the given product", @"")];
        return NO;
    }
    if (successBlock)
    {
        successBlock();
    }
    return YES;
}

প্রাপ্তি যাচাই করা হচ্ছে

রসিদ যাচাই করা নিজেই এগুলিতে সিদ্ধ হয়:

  1. প্রাপ্তিটি বৈধ পিকেসিএস 7 এবং এএসএন 1 আছে কিনা তা পরীক্ষা করা হচ্ছে। আমরা ইতিমধ্যে এটি স্পষ্টভাবে সম্পন্ন করেছি।
  2. প্রাপ্তিটি অ্যাপল স্বাক্ষর করেছে তা যাচাই করা হচ্ছে। এটি রসিদ বিশ্লেষণের আগে করা হয়েছিল এবং এটি নীচে বিস্তারিত হবে।
  3. প্রাপ্তিতে অন্তর্ভুক্ত বান্ডিল শনাক্তকারী আপনার বান্ডেল শনাক্তকারীর সাথে মিলেছে কিনা তা পরীক্ষা করা হচ্ছে। আপনার অ্যাপ্লিকেশন বান্ডেলটি সংশোধন করা এবং অন্য কিছু রসিদ ব্যবহার করা খুব কঠিন বলে মনে হচ্ছে না বলে আপনার নিজের বান্ডেল শনাক্তকারীকে হার্ডকোড করা উচিত।
  4. রসিদে অন্তর্ভুক্ত অ্যাপ্লিকেশন সংস্করণটি আপনার অ্যাপ্লিকেশন সংস্করণ শনাক্তকারীর সাথে সম্পর্কিত। উপরে উল্লিখিত একই কারণে আপনার অ্যাপ্লিকেশন সংস্করণটিকে হার্ডকোড করা উচিত।
  5. রসিদটি বর্তমান ডিভাইসের সাথে মিল রয়েছে কিনা তা নিশ্চিত করার জন্য রসিদ হ্যাশ পরীক্ষা করুন।

RMStoreAppReceiptVerificator থেকে একটি উচ্চ-স্তরে কোডের 5 টি পদক্ষেপ :

- (BOOL)verifyAppReceipt:(RMAppReceipt*)receipt
{
    // Steps 1 & 2 were done while parsing the receipt
    if (!receipt) return NO;   

    // Step 3
    if (![receipt.bundleIdentifier isEqualToString:self.bundleIdentifier]) return NO;

    // Step 4        
    if (![receipt.appVersion isEqualToString:self.bundleVersion]) return NO;

    // Step 5        
    if (![receipt verifyReceiptHash]) return NO;

    return YES;
}

আসুন ড্রিল ডাউন 2 এবং 5 ধাপে into

প্রাপ্তির স্বাক্ষর যাচাই করা হচ্ছে

যখন আমরা ডেটা বের করেছিলাম তখন আমরা রসিদ স্বাক্ষর যাচাইকরণের দিকে নজর রেখেছিলাম। প্রাপ্তিটি অ্যাপল ইনক। রুট শংসাপত্রের সাথে স্বাক্ষরিত হয়েছে, যা অ্যাপল রুট শংসাপত্র কর্তৃপক্ষ থেকে ডাউনলোড করা যেতে পারে । নিম্নলিখিত কোডটি পিকেসিএস 7 কনটেইনার এবং মূল শংসাপত্রটিকে ডেটা হিসাবে গ্রহণ করে এবং তারা মেলে কিনা তা পরীক্ষা করে:

+ (BOOL)verifyPKCS7:(PKCS7*)container withCertificateData:(NSData*)certificateData
{ // Based on: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html#//apple_ref/doc/uid/TP40010573-CH1-SW17
    static int verified = 1;
    int result = 0;
    OpenSSL_add_all_digests(); // Required for PKCS7_verify to work
    X509_STORE *store = X509_STORE_new();
    if (store)
    {
        const uint8_t *certificateBytes = (uint8_t *)(certificateData.bytes);
        X509 *certificate = d2i_X509(NULL, &certificateBytes, (long)certificateData.length);
        if (certificate)
        {
            X509_STORE_add_cert(store, certificate);

            BIO *payload = BIO_new(BIO_s_mem());
            result = PKCS7_verify(container, NULL, store, NULL, payload, 0);
            BIO_free(payload);

            X509_free(certificate);
        }
    }
    X509_STORE_free(store);
    EVP_cleanup(); // Balances OpenSSL_add_all_digests (), per http://www.openssl.org/docs/crypto/OpenSSL_add_all_algorithms.html

    return result == verified;
}

রসিদ বিশ্লেষণ করার আগে এটি শুরুতে ফিরে হয়েছিল।

প্রাপ্তি হ্যাশ যাচাই করা হচ্ছে

রসিদে অন্তর্ভুক্ত হ্যাশটি ডিভাইস আইডির একটি SHA1, রশিদে অন্তর্ভুক্ত কিছু অস্বচ্ছ মান এবং বান্ডেল আইডি।

এইভাবে আপনি আইওএসে প্রাপ্তি হ্যাশ যাচাই করবেন। আরএমএপ্পি রিসিপট থেকে :

- (BOOL)verifyReceiptHash
{
    // TODO: Getting the uuid in Mac is different. See: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html#//apple_ref/doc/uid/TP40010573-CH1-SW5
    NSUUID *uuid = [[UIDevice currentDevice] identifierForVendor];
    unsigned char uuidBytes[16];
    [uuid getUUIDBytes:uuidBytes];

    // Order taken from: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html#//apple_ref/doc/uid/TP40010573-CH1-SW5
    NSMutableData *data = [NSMutableData data];
    [data appendBytes:uuidBytes length:sizeof(uuidBytes)];
    [data appendData:self.opaqueValue];
    [data appendData:self.bundleIdentifierData];

    NSMutableData *expectedHash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH];
    SHA1(data.bytes, data.length, expectedHash.mutableBytes);

    return [expectedHash isEqualToData:self.hash];
}

এবং এটি এর সংক্ষেপে। আমি এখানে বা সেখানে কিছু অনুপস্থিত হতে পারি, তাই আমি পরে এই পোস্টে ফিরে আসতে পারি। যাইহোক, আমি আরও তথ্যের জন্য সম্পূর্ণ কোড ব্রাউজ করার পরামর্শ দিচ্ছি।


2
সুরক্ষা অস্বীকার: ওপেন সোর্স কোড ব্যবহার করা আপনার অ্যাপ্লিকেশনটিকে আরও দুর্বল করে তোলে। সুরক্ষা যদি উদ্বেগের বিষয় হয় তবে আপনি কেবল গাইড হিসাবে আরএমএসটোর এবং উপরের কোডটি ব্যবহার করতে চাইতে পারেন।
এইচপিক

6
ভবিষ্যতে যদি আপনি ওপেনএসএসএল থেকে মুক্তি পান এবং কেবল সিস্টেম ফ্রেমওয়ার্ক ব্যবহার করে আপনার লাইব্রেরিটিকে সংক্ষিপ্ত করেন তবে এটি দুর্দান্ত হবে।
হাঁসের

2
@ রাবারডাক github.com/robotmedia/RMStore/issues/16 দেখুন । চিম বা নির্দ্বিধায় নির্দ্বিধায়। :)
এইচপিক

1
@ রাবারডাক এ পর্যন্ত আমার ওপেনএসএসএল সম্পর্কে শূন্য জ্ঞান ছিল। কে জানে, আপনি এটি পছন্দ করতে পারে। : পি
hpique

2
মিডল অ্যাটাকের কোনও ম্যানের পক্ষে এটি সংবেদনশীল, যেখানে অনুরোধ এবং / অথবা প্রতিক্রিয়াটি বাধা দেওয়া ও সংশোধন করা যেতে পারে। উদাহরণস্বরূপ, অনুরোধটি একটি তৃতীয় পক্ষের সার্ভারে পুনঃনির্দেশিত করা যেতে পারে, এবং একটি মিথ্যা প্রতিক্রিয়া ফিরে পাওয়া যেতে পারে, কোনও পণ্য কেনা হয়েছিল তা ভেবে অ্যাপ্লিকেশনটিকে চালিত করে, যখন তা ছিল না এবং বিনামূল্যে কার্যকারিতা সক্ষম করে।
জাসারিএন

13

আমি অবাক হয়েছি এখানে কেউ রেসিগেনের কথা উল্লেখ করেনি । এটি এমন একটি সরঞ্জাম যা স্বয়ংক্রিয়ভাবে আপত্তিজনক রসিদ বৈধকরণ কোড উত্পন্ন করে, যা প্রতিবারের চেয়ে আলাদা; এটি জিইউআই এবং কমান্ড-লাইন অপারেশন উভয়কেই সমর্থন করে। অত্যন্ত বাঞ্ছনীয়.

(রিসিগেনের সাথে অনুমোদিত নয়, কেবল একটি সুখী ব্যবহারকারী))

আমি টাইপ করার সময় রিসিজেনকে স্বয়ংক্রিয়ভাবে পুনরায় চালনার জন্য আমি এই জাতীয় একটি রেকফিল ব্যবহার করি (কারণ এটি প্রতিটি সংস্করণ পরিবর্তনে করা দরকার) rake receigen:

desc "Regenerate App Store Receipt validation code using Receigen app (which must be already installed)"
task :receigen do
  # TODO: modify these to match your app
  bundle_id = 'com.example.YourBundleIdentifierHere'
  output_file = File.join(__dir__, 'SomeDir/ReceiptValidation.h')

  version = PList.get(File.join(__dir__, 'YourProjectFolder/Info.plist'), 'CFBundleVersion')
  command = %Q</Applications/Receigen.app/Contents/MacOS/Receigen --identifier #{bundle_id} --version #{version} --os ios --prefix ReceiptValidation --success callblock --failure callblock>
  puts "#{command} > #{output_file}"
  data = `#{command}`
  File.open(output_file, 'w') { |f| f.write(data) }
end

module PList
  def self.get file_name, key
    if File.read(file_name) =~ %r!<key>#{Regexp.escape(key)}</key>\s*<string>(.*?)</string>!
      $1.strip
    else
      nil
    end
  end
end

1
যারা রিসিগেনে আগ্রহী তাদের জন্য এটি একটি প্রদত্ত সমাধান, যা অ্যাপ স্টোরে 29.99 99 এ উপলব্ধ $ যদিও সেপ্টেম্বর 2014. যেহেতু আপডেট করা হয় নি
DevGansta

সত্য, আপডেটের অভাব খুব উদ্বেগজনক। তবে এটি এখনও কাজ করে; FWIW, আমি এটি আমার অ্যাপ্লিকেশনগুলিতে ব্যবহার করছি in
আন্দ্রে তারানসনভ

ফাঁস হওয়ার জন্য যন্ত্রগুলিতে আপনার অ্যাপ্লিকেশনটি পরীক্ষা করুন, রিসিগেনের সাথে আমি তাদের প্রচুর পেয়েছি।
শ্রদ্ধেয়

রিসিগেন হ'ল কাটিয়া প্রান্ত, তবে হ্যাঁ এটি লজ্জাজনক বলে মনে হয় এটি বাদ পড়েছে।
ফ্যাটি

1
দেখে মনে হচ্ছে এটি এখনও বাদ হয়নি। তিন সপ্তাহ আগে আপডেট হয়েছে!
ওলেগ কোরঝুকভ

2

দ্রষ্টব্য: ক্লায়েন্ট পক্ষেই এই ধরণের যাচাইকরণ করার পরামর্শ দেওয়া হয় না

অ্যাপ্লিকেশন-কেনার রশিদের বৈধতার জন্য এটি একটি সুইফট 4 সংস্করণ ...

প্রাপ্তি বৈধতার সম্ভাব্য ত্রুটিগুলি উপস্থাপন করার জন্য একটি এনাম তৈরি করতে দেয়

enum ReceiptValidationError: Error {
    case receiptNotFound
    case jsonResponseIsNotValid(description: String)
    case notBought
    case expired
}

তারপরে রশিদটিকে বৈধতা দেয় এমন ফাংশন তৈরি করি, এটি বৈধতা প্রমাণ করতে অক্ষম হলে এটি একটি ত্রুটি ছুঁড়ে দেয়।

func validateReceipt() throws {
    guard let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager.default.fileExists(atPath: appStoreReceiptURL.path) else {
        throw ReceiptValidationError.receiptNotFound
    }

    let receiptData = try! Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
    let receiptString = receiptData.base64EncodedString()
    let jsonObjectBody = ["receipt-data" : receiptString, "password" : <#String#>]

    #if DEBUG
    let url = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")!
    #else
    let url = URL(string: "https://buy.itunes.apple.com/verifyReceipt")!
    #endif

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = try! JSONSerialization.data(withJSONObject: jsonObjectBody, options: .prettyPrinted)

    let semaphore = DispatchSemaphore(value: 0)

    var validationError : ReceiptValidationError?

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, let httpResponse = response as? HTTPURLResponse, error == nil, httpResponse.statusCode == 200 else {
            validationError = ReceiptValidationError.jsonResponseIsNotValid(description: error?.localizedDescription ?? "")
            semaphore.signal()
            return
        }
        guard let jsonResponse = (try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [AnyHashable: Any] else {
            validationError = ReceiptValidationError.jsonResponseIsNotValid(description: "Unable to parse json")
            semaphore.signal()
            return
        }
        guard let expirationDate = self.expirationDate(jsonResponse: jsonResponse, forProductId: <#String#>) else {
            validationError = ReceiptValidationError.notBought
            semaphore.signal()
            return
        }

        let currentDate = Date()
        if currentDate > expirationDate {
            validationError = ReceiptValidationError.expired
        }

        semaphore.signal()
    }
    task.resume()

    semaphore.wait()

    if let validationError = validationError {
        throw validationError
    }
}

একটি নির্দিষ্ট পণ্যের মেয়াদোত্তীকরণের তারিখ পেতে, এই সহায়ক ফাংশনটি ব্যবহার করি। ফাংশনটি একটি JSON প্রতিক্রিয়া এবং একটি পণ্য আইডি পেয়েছে। জেএসওএন প্রতিক্রিয়াতে বিভিন্ন পণ্যগুলির একাধিক প্রাপ্তির তথ্য থাকতে পারে, সুতরাং এটি নির্দিষ্ট প্যারামিটারের জন্য সর্বশেষ তথ্য পায়।

func expirationDate(jsonResponse: [AnyHashable: Any], forProductId productId :String) -> Date? {
    guard let receiptInfo = (jsonResponse["latest_receipt_info"] as? [[AnyHashable: Any]]) else {
        return nil
    }

    let filteredReceipts = receiptInfo.filter{ return ($0["product_id"] as? String) == productId }

    guard let lastReceipt = filteredReceipts.last else {
        return nil
    }

    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"

    if let expiresString = lastReceipt["expires_date"] as? String {
        return formatter.date(from: expiresString)
    }

    return nil
}

এখন আপনি এই ফাংশনটি কল করতে এবং সম্ভাব্য ত্রুটির ক্ষেত্রে পরিচালনা করতে পারেন

do {
    try validateReceipt()
    // The receipt is valid 😌
    print("Receipt is valid")
} catch ReceiptValidationError.receiptNotFound {
    // There is no receipt on the device 😱
} catch ReceiptValidationError.jsonResponseIsNotValid(let description) {
    // unable to parse the json 🤯
    print(description)
} catch ReceiptValidationError.notBought {
    // the subscription hasn't being purchased 😒
} catch ReceiptValidationError.expired {
    // the subscription is expired 😵
} catch {
    print("Unexpected error: \(error).")
}

আপনি অ্যাপ স্টোর কানেক্ট থেকে পাসওয়ার্ড পেতে পারেন । https://developer.apple.comএই লিঙ্কটি ক্লিক করুন ক্লিক করুন

  • Account tab
  • Do Sign in
  • Open iTune Connect
  • Open My App
  • Open Feature Tab
  • Open In App Purchase
  • Click at the right side on 'View Shared Secret'
  • At the bottom you will get a secrete key

সেই কীটি অনুলিপি করুন এবং পাসওয়ার্ড ক্ষেত্রে পেস্ট করুন।

আশা করি এটি যারা সুইফ্ট সংস্করণে চায় তাদের প্রত্যেকের জন্য সহায়তা করবে।


19
আপনার ডিভাইস থেকে কখনও অ্যাপল বৈধতা URL ব্যবহার করা উচিত নয়। এটি কেবল আপনার সার্ভার থেকে ব্যবহার করা উচিত। ডাব্লুডাব্লুডিসি অধিবেশনগুলিতে এটি উল্লেখ করা হয়েছিল।
পেচার

ব্যবহারকারী অ্যাপগুলি মুছে ফেললে বা দীর্ঘ সময় না খেলে কী হবে? আপনার মেয়াদ শেষ হওয়ার তারিখ গণনা কি ঠিকঠাক কাজ করছে?
কার্তিকেইয়ান

তারপরে আপনার সার্ভারের পাশে বৈধতা রাখা দরকার।
পুষ্পেন্দ্র

1
@ পেচার যেমন বলেছে, আপনার কখনই এটি করা উচিত নয়। দয়া করে আপনার উত্তরের শীর্ষে এটি যুক্ত করুন।
ডাব্লুডব্লিউডিসি

ডিভাইস থেকে সরাসরি রসিদ ডেটা প্রেরণ করা কেন সুরক্ষিত নয় তা আমি বুঝতে পারি না। কেউ কি ব্যাখ্যা করতে সক্ষম হবে?
কোহ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.