Subscriptions (Recurring API)

Nets EASY supports subscriptions allowing charging customers on a regular basis. The API contains subscription creation and charging of subscriptions. Merchant is responsible for managing subscriptions after they’re created with Nets EASY recurring API. The following code samples demonstrate the steps required to create and charge subscriptions.

Create a Subscription with Nets EASY Recurring API

To create a subscription, include a subscription JSON object in the request body of the POST payment request as shown in Nets Easy API reference.

IMPORTANT: While making the API call for creating a subscription , make sure you add "commercePlatformTag" : "iOSSDK" in the request header. This is critical to identify the platform from which the payment is initiated.

let headers: HTTPHeaders = [
    "Authorization": token,
    "Content-Type": "application/json",
    "commercePlatformTag" : "iOSSDK"
]

/// Subscription request body
struct SubscriptionRequest: Encodable {
    let order: Order
    let checkout: Checkout
    let merchantNumber: String?
    let notifications: String?
    let subscription: Subscription

    struct Subscription: Encodable {
        let endDate: String 
        let interval: Int = 0
    }
}

/// Response object following subscription creation
struct SubscriptionRegistration: Decodable {
    let paymentId: String
    let hostedPaymentPageUrl: String
}

// E.g. subscription creation request. See MiaSample app for more
func createSubscription(success: @escaping (SubscriptionRegistration) -> Void) {
    let url = baseURL.appending(path: "payments/")!
    var request = URLRequest(for: url, method: .post, headers: authorizationHeaders)
    request.httpBody = try! JSONEncoder().encode(SubscriptionRequest())
    URLSession.shared.fetchJson(with: request, decodeDelegate: self, success: success)
}

A subscription object contains an endDate, last date of the subscription, and an interval representing the interval (in number of days) at which the subscription can be charged.
For recurring payments chargeable at any given time (with no regular interval), interval = 0 should be used.

Nets EASY recurring API expects the endDate to have the following format: yyyy-MM-dd'T'HH:mm:ss+zz:zz.

e.g. 2019-07-18T00:00:00+01:00 for timezone GMT + 1 hour.

The following extension on DateFormatter can be used to convert a given date and timezone to this format:

extension DateFormatter {
    /// Returns date formatted for Nets EASY recurring API
    /// e.g. output: "2019-07-18T00:00:00+00:00"
    static func easyAPIFormatted(_ date: Date, timezone: TimeZone = .current) -> String {
        let timezoneOffset: String = {
            DateFormatter.shared.timeZone = timezone
            let seconds = DateFormatter.shared.timeZone.secondsFromGMT()
            let hours = seconds / 3600
            let minutes = abs(seconds / 60 % 60)
            return String(format: "%+.2d:%.2d", hours, minutes)
        }()
        return DateFormatter.shared.string(from: date) + timezoneOffset
    }

    private static let shared: DateFormatter = {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
        return dateFormatter
    }()
}

Retrieve a Newly Created Subscription

Nets EASY recurring API returns paymentID following subscription creation. To obtain the subscription details, use this ID to fetch subscription ID for the newly created subscription.

See Nets Easy API reference

/// Response object following fetch request of subscription with `paymentId`
/// Object contains subscription ID that can be used to fetch subscription details
struct SubscriptionPaymentDetails: Decodable {
    let payment: Payment

    struct Payment: Decodable {
        let created: String
        let subscription: Subscription
    }

    struct Subscription: Decodable { let id: String }
}

// E.g. payment (for subscription) request. See MiaSample app for more
func fetchSubscriptionPayment(success: success: @escaping (SubscriptionPaymentDetails) -> Void) {
    let url = baseURL.appending(path: "payments/\(paymentID)")!
    let request = URLRequest(for: url, method: .get, headers: headers)
    URLSession.shared.fetchJson(with: request, decodeDelegate: self, success: success, failure: nil)
}

Subscription details can be obtained from Nets EASY using a subscription ID.

// E.g. subscription details fetch request. See MiaSample app for more
func fetchSubscriptionDetails(subscriptionID: String, success: @escaping (SubscriptionDetails) -> Void) {
    let url = baseURL.appending(path: "subscriptions/\(subscriptionID)")!
    let request = URLRequest(for: url, method: .get, headers: headers)
    URLSession.shared.fetchJson(with: request, decodeDelegate: self, success: success, failure: failure)
}

/// Subscription Details - Response to subscriptions fetch for given `subscriptionID`
struct SubscriptionDetails: Decodable {
    let subscriptionId: String
    let frequency, interval: Int
    let endDate: String
    let paymentDetails: PaymentDetails
}

Charging Subscription

Subscriptions can be charged via a POST charge request providing subscription ID and a charge request body containing order details.

See Nets Easy API reference

/// Success response following charge-subscription request
struct ChargeSubscriptionResponse: Decodable {
    let paymentId: String, chargeId: String
}

// E.g. charge request. See MiaSample app for more
func chargeSubscription(subscriptionID: String, success: @escaping (ChargeSubscriptionResponse) -> Void) {
    let url = baseURL.appending(path: "subscriptions/\(subscriptionID)/charges")!
    var request = URLRequest(for: url, method: .post, headers: headers)
    struct ChargeRequest: Encodable { let order: Order }
    request.httpBody = try! JSONEncoder().encode(ChargeRequest(order: TheOrder))
    URLSession.shared.fetchJson(with: request, decodeDelegate: self, success: success, failure: failure)
}

After creating a subscription, merchant is responsible for storing and managing the subscription.

(Note that the MiaSample app stores subscriptions in the device for demo purposes but we recommend your app backend for storage)