Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove password from ReceiptValidator protocol as this is specific to AppleReceiptValidator #281

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

All notable changes to this project will be documented in this file.

## [0.10.9](https://github.com/bizz84/SwiftyStoreKit/releases/tag/0.10.9) Add `fetchReceipt` method
## [0.11.0](https://github.com/bizz84/SwiftyStoreKit/releases/tag/0.11.0) Add `fetchReceipt` method + update `verifyReceipt` and `ReceiptValidator` protocol

* Add `fetchReceipt` method. Update `verifyReceipt` to use it ([#278](https://github.com/bizz84/SwiftyStoreKit/pull/278), related issues: [#272](https://github.com/bizz84/SwiftyStoreKit/issues/272), [#223](https://github.com/bizz84/SwiftyStoreKit/issues/223)).
* Remove `password` from `ReceiptValidator` protocol as this is specific to `AppleReceiptValidator` ([#281](https://github.com/bizz84/SwiftyStoreKit/pull/281/)). **Note**: This is an API breaking change.


## [0.10.8](https://github.com/bizz84/SwiftyStoreKit/releases/tag/0.10.8) Update to swiftlint 0.22.0

Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,8 @@ If `fetchReceipt` is successful, it will return the **encrypted** receipt as a s
Use this method to (optionally) refresh the receipt and perform validation in one step.

```swift
let appleValidator = AppleReceiptValidator(service: .production)
SwiftyStoreKit.verifyReceipt(using: appleValidator, password: "your-shared-secret", forceRefresh: false) { result in
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in
switch result {
case .success(let receipt):
print("Verify receipt Success: \(receipt)")
Expand Down Expand Up @@ -353,8 +353,8 @@ If you need to verify multiple purchases / subscriptions, you can either:
### Verify Purchase

```swift
let appleValidator = AppleReceiptValidator(service: .production)
SwiftyStoreKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
switch result {
case .success(let receipt):
// Verify the purchase of Consumable or NonConsumable
Expand Down Expand Up @@ -387,8 +387,8 @@ From [Apple - Working with Subscriptions](https://developer.apple.com/library/co
When one or more subscriptions are found for a given product id, they are returned as a `ReceiptItem` array ordered by `expiryDate`, with the first one being the newest.

```swift
let appleValidator = AppleReceiptValidator(service: .production)
SwiftyStoreKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
switch result {
case .success(let receipt):
// Verify the purchase of a Subscription
Expand Down Expand Up @@ -448,8 +448,8 @@ SwiftyStoreKit.purchaseProduct(productId, atomically: true) { result in
SwiftyStoreKit.finishTransaction(purchase.transaction)
}

let appleValidator = AppleReceiptValidator(service: .production)
SwiftyStoreKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in

if case .success(let receipt) = result {
let purchaseResult = SwiftyStoreKit.verifySubscription(
Expand Down
5 changes: 2 additions & 3 deletions SwiftyStoreKit-iOS-Demo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,8 @@ class ViewController: UIViewController {

func verifyReceipt(completion: @escaping (VerifyReceiptResult) -> Void) {

let appleValidator = AppleReceiptValidator(service: .production)
let password = "your-shared-secret"
SwiftyStoreKit.verifyReceipt(using: appleValidator, password: password, completion: completion)
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator, completion: completion)
}

func verifyPurchase(_ purchase: RegisteredPurchase) {
Expand Down
5 changes: 2 additions & 3 deletions SwiftyStoreKit-macOS-Demo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,8 @@ class ViewController: NSViewController {

func verifyReceipt(completion: @escaping (VerifyReceiptResult) -> Void) {

let appleValidator = AppleReceiptValidator(service: .production)
let password = "your-shared-secret"
SwiftyStoreKit.verifyReceipt(using: appleValidator, password: password, completion: completion)
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator, completion: completion)
}

func verifyPurchase(_ purchase: RegisteredPurchase) {
Expand Down
24 changes: 14 additions & 10 deletions SwiftyStoreKit/AppleReceiptValidator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,28 @@ public struct AppleReceiptValidator: ReceiptValidator {
case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt"
}

public init(service: VerifyReceiptURLType = .production) {
private let service: VerifyReceiptURLType
private let sharedSecret: String?

/**
* Reference Apple Receipt Validator
* - Parameter service: Either .production or .sandbox
* - Parameter sharedSecret: Only used for receipts that contain auto-renewable subscriptions. Your app’s shared secret (a hexadecimal string).
*/
public init(service: VerifyReceiptURLType = .production, sharedSecret: String? = nil) {
self.service = service
self.sharedSecret = sharedSecret
}

private let service: VerifyReceiptURLType

public func validate(
receipt: String,
password autoRenewPassword: String? = nil,
completion: @escaping (VerifyReceiptResult) -> Void) {
public func validate(receipt: String, completion: @escaping (VerifyReceiptResult) -> Void) {

let storeURL = URL(string: service.rawValue)! // safe (until no more)
let storeRequest = NSMutableURLRequest(url: storeURL)
storeRequest.httpMethod = "POST"

let requestContents: NSMutableDictionary = [ "receipt-data": receipt ]
// password if defined
if let password = autoRenewPassword {
if let password = sharedSecret {
requestContents.setValue(password, forKey: "password")
}

Expand Down Expand Up @@ -101,8 +105,8 @@ public struct AppleReceiptValidator: ReceiptValidator {
*/
let receiptStatus = ReceiptStatus(rawValue: status) ?? ReceiptStatus.unknown
if case .testReceipt = receiptStatus {
let sandboxValidator = AppleReceiptValidator(service: .sandbox)
sandboxValidator.validate(receipt: receipt, password: autoRenewPassword, completion: completion)
let sandboxValidator = AppleReceiptValidator(service: .sandbox, sharedSecret: self.sharedSecret)
sandboxValidator.validate(receipt: receipt, completion: completion)
} else {
if receiptStatus.isValid {
completion(.success(receipt: receiptInfo))
Expand Down
13 changes: 4 additions & 9 deletions SwiftyStoreKit/InAppReceiptVerificator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,19 @@ class InAppReceiptVerificator: NSObject {
/**
* Verify application receipt.
* - Parameter validator: Validator to check the encrypted receipt and return the receipt in readable format
* - Parameter password: Your app’s shared secret (a hexadecimal string). Only used for receipts that contain auto-renewable subscriptions.
* - Parameter forceRefresh: If true, refreshes the receipt even if one already exists.
* - Parameter refresh: closure to perform receipt refresh (this is made explicit for testability)
* - Parameter completion: handler for result
*/
public func verifyReceipt(using validator: ReceiptValidator,
password: String? = nil,
forceRefresh: Bool,
refresh: InAppReceiptRefreshRequest.ReceiptRefresh = InAppReceiptRefreshRequest.refresh,
completion: @escaping (VerifyReceiptResult) -> Void) {

fetchReceipt(forceRefresh: forceRefresh, refresh: refresh) { result in
switch result {
case .success(let encryptedReceipt):
self.verify(receipt: encryptedReceipt, using: validator, password: password, completion: completion)
self.verify(receipt: encryptedReceipt, using: validator, completion: completion)
case .error(let error):
completion(.error(error: error))
}
Expand All @@ -79,9 +77,7 @@ class InAppReceiptVerificator: NSObject {
completion: @escaping (FetchReceiptResult) -> Void) {

if let receiptData = appStoreReceiptData, forceRefresh == false {

fetchReceiptSuccessHandler(receiptData: receiptData, completion: completion)

} else {

receiptRefreshRequest = refresh(nil) { result in
Expand Down Expand Up @@ -111,12 +107,11 @@ class InAppReceiptVerificator: NSObject {
/**
* - Parameter receiptData: encrypted receipt data
* - Parameter validator: Validator to check the encrypted receipt and return the receipt in readable format
* - Parameter password: Your app’s shared secret (a hexadecimal string). Only used for receipts that contain auto-renewable subscriptions.
* - Parameter completion: handler for result
*/
private func verify(receipt: String, using validator: ReceiptValidator, password: String? = nil, completion: @escaping (VerifyReceiptResult) -> Void) {

validator.validate(receipt: receipt, password: password) { result in
private func verify(receipt: String, using validator: ReceiptValidator, completion: @escaping (VerifyReceiptResult) -> Void) {
validator.validate(receipt: receipt) { result in

DispatchQueue.main.async {
completion(result)
Expand Down
2 changes: 1 addition & 1 deletion SwiftyStoreKit/SwiftyStoreKit+Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public struct PurchaseDetails {

//Conform to this protocol to provide custom receipt validator
public protocol ReceiptValidator {
func validate(receipt: String, password autoRenewPassword: String?, completion: @escaping (VerifyReceiptResult) -> Void)
func validate(receipt: String, completion: @escaping (VerifyReceiptResult) -> Void)
}

// Payment transaction
Expand Down
5 changes: 2 additions & 3 deletions SwiftyStoreKit/SwiftyStoreKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,12 @@ extension SwiftyStoreKit {
/**
* Verify application receipt
* - Parameter validator: receipt validator to use
* - Parameter password: Only used for receipts that contain auto-renewable subscriptions. Your app’s shared secret (a hexadecimal string).
* - Parameter forceRefresh: If true, refreshes the receipt even if one already exists.
* - Parameter completion: handler for result
*/
public class func verifyReceipt(using validator: ReceiptValidator, password: String? = nil, forceRefresh: Bool = false, completion: @escaping (VerifyReceiptResult) -> Void) {
public class func verifyReceipt(using validator: ReceiptValidator, forceRefresh: Bool = false, completion: @escaping (VerifyReceiptResult) -> Void) {

sharedInstance.receiptVerificator.verifyReceipt(using: validator, password: password, forceRefresh: forceRefresh, completion: completion)
sharedInstance.receiptVerificator.verifyReceipt(using: validator, forceRefresh: forceRefresh, completion: completion)
}

/**
Expand Down
18 changes: 9 additions & 9 deletions SwiftyStoreKitTests/InAppReceiptVerificatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import XCTest

class TestReceiptValidator: ReceiptValidator {
var validateCalled = false
func validate(receipt: String, password autoRenewPassword: String?, completion: @escaping (VerifyReceiptResult) -> Void) {
func validate(receipt: String, completion: @escaping (VerifyReceiptResult) -> Void) {
validateCalled = true
completion(.success(receipt: [:]))
}
Expand Down Expand Up @@ -76,7 +76,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)

var refreshCalled = false
verificator.verifyReceipt(using: validator, password: nil, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

refreshCalled = true
return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
Expand All @@ -95,7 +95,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)

var refreshCalled = false
verificator.verifyReceipt(using: validator, password: nil, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

refreshCalled = true
return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
Expand All @@ -115,7 +115,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)

var refreshCalled = false
verificator.verifyReceipt(using: validator, password: nil, forceRefresh: true, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: true, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

refreshCalled = true
return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
Expand All @@ -132,7 +132,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
let refreshError = NSError(domain: "", code: 0, userInfo: nil)

verificator.verifyReceipt(using: validator, password: nil, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

callback(.error(e: refreshError))
return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
Expand All @@ -148,7 +148,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let validator = TestReceiptValidator()
let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)

verificator.verifyReceipt(using: validator, password: nil, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

callback(.success)
return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
Expand All @@ -167,7 +167,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let validator = TestReceiptValidator()
let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)

verificator.verifyReceipt(using: validator, password: nil, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

writeReceiptData(to: testReceiptURL)
callback(.success)
Expand All @@ -188,7 +188,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let validator = TestReceiptValidator()
let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)

verificator.verifyReceipt(using: validator, password: nil, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

writeReceiptData(to: testReceiptURL)
callback(.success)
Expand All @@ -210,7 +210,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
let validator = TestReceiptValidator()
let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)

verificator.verifyReceipt(using: validator, password: nil, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in

XCTFail("refresh should not be called if we already have a receipt")
return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
Expand Down