Page MenuHomeDatingVIP

Mobile Integration
Updated 2,459 Days AgoPublic

Google Play integration

Operations

1. Initial Purchase

Some action will be called on Dating Frontend when user purchases subscription.
User's purchase needs to be verified.
Idea here is to make transaction.init api call to Payment.
After transaction.init api call, we make transaction.finish api call to Payment.
Payment will verify purchase inside transaction.init api call and based on verification transaction.finish will succeed or fail.
Verification includes calling Google Play api - Purchases.subscriptions , get.
Both api calls (transaction.init and transaction.finish) happen inside 1 request, ie it is similar to how Credit Card payment flows, ie. there is no need to use notifications to create user's subscription - it is instant.

Verification is done using Purchases.subscriptions: get api call to Google.

Flow:

gp mock.png (722×1 px, 47 KB)

2. Rebill

Using rebill schedule on Payment.
Since we know when subscription should expire, we can schedule rebill at that time.
Rebill procedure will just check subscription status on Google and if subscription is expired - we know it is stopped on Google and we should stop it on Payment and on Dating (using notifications), otherwise - subscription is still active which means that the Rebill happened (Google does all billing).
We're using somewhat generous rebill schedule for start - allowing failed retries, third stops subscription.

Rebill will make this Purchases.subscriptions: get api call to Google.

3. Stop

Purchases.subscriptions: cancel is that. It should be valid for subscriptions (Premium).
It will just cancel subscription. Subscription will expire after currently paid period.

4. Refund

Purchases.subscriptions: revoke - this is Stop & refund option that we use. It will refund and immediately expire user's subscription.

5. Edit

TODO / TBD (limited options for subscriptions - defer call)

Server

Frontend

1. Initial purchase action

Parameters from mobile app:
Must have:

  • packageName string The package name of the application for which this subscription was purchased (for example, 'com.some.thing').
  • subscriptionId string The purchased subscription ID (for example, 'monthly001').
  • token string The token provided to the user's device when the subscription was purchased.

Other parameters can be added as needed by Mobile app or server app

We use subscriptionId's like: m1_2534_2_premium, m1_2534_14_premium, m1_2534_160_premium
system_id.site_id.pay_item_id.payment_item_type - should be string that has meaning on server app.

Admin

1. Refund

Config mostly, maybe some smaller code updates.

2. Stop, Cancel

Config mostly, maybe some smaller code updates.

3. Refund

Config mostly, maybe some smaller code updates.

Payment

1. Initial purchase

Must:

2. Rebill

3. Stop

4. Refund

Transactions

Google deals with financial details, ie we do not get any transaction identifiers, amounts ...
For all operations that we save as transactions on Payment (initial purchase, rebill, refund) we can fill transactions table record.
Transactions details can be stored in fixed fields table (not using json) by having all fields for all responses for all api commands that we call (there should not be too many of them).
All possible response keys from these 2 apis should be keys in transaction details table:

Refund via Google api is only possible for last subscription charge. Other transactions can be refunded from some Google Admin

Google Parameters for each item

Each call to Google Play API requires 3 params:

  • packageName string The package name of the application for which this subscription was purchased (for example, 'com.some.thing').
  • subscriptionId string The purchased subscription ID (for example, 'monthly001').
  • token string The token provided to the user's device when the subscription was purchased.

We get these values when initial purchase action on Dating is called, and then they are passed to Payment in transaction.finish call.
These values are per item. We can store them in some additional table (user_id + item_id + these params).

Google Authorization

Service account with key (json) from https://console.cloud.google.com/iam-admin/serviceaccounts/project?project=myproject should be parsed and filled to appropriate pp_params_* table

Testing environment

None


Verification

Google Play verification is done using this lib: https://github.com/google/google-api-php-client(GooglePlayDevelopers API). It's pretty much well documented on what/how to do.

Ios needs call to their store service with password and receipt-data(appStoreReceipt, not transactionReceipt) sent in request payload. There basically receipt-data is decrypted into array of all receipts ever made for that account and returned for verification. Verification is done using this: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1 - maybe a bit confusing with all field changes and deprecation - for auto-renewing subscriptions should be enough to check only status - if it's 0 -> subscription is OK, if 21006 -> expired. Non renewing subscriptions would need for server app to keep track of subscriptions, expiry dates, etc.

Settings

Settings and data needed for verification:

  • for GooglePlay we use Service Accounts(Google Cloud Platform -> IAM & Admin) (seemed easier to implement verification using Service Accounts, with above mentioned php lib). So, one should be created for android app("Editor" role is fine) with private key in json format - that json key is provided to above mentioned php lib for verification. Also, Google Play Developers API should be enabled for that app(Google Cloud Platform -> API Manager->Library, under Mobile APIs)
  • for Ios, for auto-renewable subscriptions password is needed - app’s shared secret (a hexadecimal string).

Payment API usage

Usage is similar to Credit Card purchase, only different fields are sent. First call to transaction.init, then transaction.finish. In transaction.finish instead of regular payment, we check for successful payment on GooglePlay. If check is successful - new transaction/subscription is created.

transaction.init

Similar to Credit Card payment, pretty much all relevant fields are used for mobile integration as well.

Input Parameters
NameTypeRequiredDescriptionNote
pp_type(string)yesPayment processor typeGP for GooglePlay, IO for Ios(Apple)
tracking_order(int)noOrder ID to be tracked on client app.
tracking_user(int)yesUser ID to be tracked on client app.
tracking_tag(int)yesTag ID to be tracked on client app.
first_name(string)yesFirst Name
last_name(string)yesLast Name
zipcode(string)yesUser ZIP code
country(string)yesCountry abbr.
email(string)yesUser email address
ip(string)yesIP address from which user is making the transaction
host(string)yesHost form which user is making the transaction
currency(string)yesCurrency abbr.
order_type(string)noAllowed values '' (empty string) and xsale. Determine if order is Cross Sale or not.
return_url(string)noReturn URL (used for PayPal like PP's)Not used
cancel_url(string)noCancel URL (used for PayPal like PP's)Not used
items*(array)yesArray of itemsShould be created to match service offered in Mobile App
override(int)noUsed for Payment Processor Account OverrideNot used
override_tag(int)noUsed for Payment Processor Account OverrideNot used
override_type(string)noAllowed values: '' (empty string) and x-sales. See Payment Processor Account OverrideNot used

items*

NameTypeRequiredDescription
trial_amountfloatnoTrial Amount
trial_unitstring (day|week|month|year)noTrial unit
trial_periodintnoTrial period
amountfloatyesrebill amount
rebill_unitstring (day|week|month|year)yesrebill unit
rebill_periodintyesrebill period
max_rebill_countintyesmax rebill count (-1 unlimited)
descriptionstringyesItem description
NOTE: Items should be exact copy of services offered in mobile app - prices and duration must match.
Return
NameTypeDescription
resultarrayResult array with data
NameTypeDescription
HashString(32)Order identification hash - for usage in transaction.finish
RedirectvoidNot in use

Example Request:

{
    "command": "transaction.init",
    "data": {
        "tracking_order": "0",
        "tracking_user": "4152905",
        "tracking_tag": "14",
        "zipcode": "34206",
        "country": "RS",
        "email": "zjskwsj@gmail.com",
        "first_name": "zjskwsj",
        "last_name": "",
        "ip": "46.40.22.12",
        "host": "12-22-40-46.dynamic.stcable.net",
        "currency": "RSD",
        "pp_type": "GP",
        "tax_rate": "0",
        "tax_amount": "0",
        "items": [
            {
                "tracking_item": "172",
                "amount": "2999.99",
                "rebill_unit": "day",
                "rebill_period": "1",
                "trial_amount": "0.00",
                "trial_unit": "day",
                "trial_period": "0",
                "max_rebill_count": "-1",
                "description": "Mobile app test"
            }
        ],
        "order_type": "",
        "override_type": ""
    },
    "token": "dating-frontend-m1@staging",
    "password": "26ff2170e0034d29d702c65d888aaec1"
}

Example Response:

{
    "code": "700",
    "status": "Action completed successfully",
    "command": "transaction.init",
    "result": {
        "hash": "148069xxxxxx3599bd6a0e891c4bdadc",
        "redirect": null
    },
    "request": {
        "command": "transaction.init",
        "data": {
            "tracking_order": "0",
            "tracking_user": "4152905",
            "tracking_tag": "14",
            "zipcode": "34206",
            "country": "RS",
            "email": "zjskwsj@gmail.com",
            "first_name": "zjskwsj",
            "last_name": "",
            "ip": "46.40.22.12",
            "host": "12-22-40-46.dynamic.stcable.net",
            "currency": "RSD",
            "pp_type": "GP",
            "tax_rate": "0",
            "tax_amount": "0",
            "items": [
                {
                    "tracking_item": "172",
                    "amount": "2999.99",
                    "rebill_unit": "day",
                    "rebill_period": "1",
                    "trial_amount": "0.00",
                    "trial_unit": "day",
                    "trial_period": "0",
                    "max_rebill_count": "-1",
                    "description": "Mobile app test"
                }
            ],
            "order_type": "",
            "override_type": ""
        },
        "token": "dating-frontend-m1@staging",
        "password": "26ff2170e0034d29d702c65d888aaec1"
    },
    "ts": 1480696496,
    "origin_ip": "69.61.28.237",
    "errors": null
}

transaction.finish

Finish transaction that has been started previously. Check if payment exists on GooglePlay/iTunes. If exists create subscription/transaction.

Input Parameters

Input parameters and response differ for GooglePlay/iTunes.

GooglePlay
NameTypeRequiredDescriptionExample
hash(string)yesHash string of order that is related to transaction1480691234563599bd6a0e891c4bdadc
pp_type(string)yesPayment processor typeGP,IO
purchase_id(string)yesName of the service as created on GooglePlay. Should be created so it has meaning for server app. It's just name for payment app that will be used in payment checksm1_14_123_premium
purchase_token(string)yesVerification string that GooglePlay provides to mobile appdlpjpcgmhpgnpigfnnionmka.AO-J1Ox-ylRzN4odIalERHQDclB2KA6Fbyzp_Os0T6h1FBAtqd1IsgiuzuvBWI5nKPSpmzRdi3JA-fH6jtO2JdCq5KimYzRa4eWoj50MC1Txk4hXyYcq4Lk4TNLNym-1Tqy2AxRZKXTk
package_name(string)yesMobile app's package namecom.mydomain.mypackage
purchase_type(string)yesPurchase type. Can be 0 or 10 for subscription, 1 for product
merchant_order_number(string)yesOrder ID on GooglePlay service - Not used. If don't have it in receipt, send 0.0[merchant_id](20).[order_id](17)

Example Request:

{
    "command": "transaction.finish",
    "data": {
        "hash": "148069xxxxxx3599bd6a0e891c4bdadc",
        "pp_type": "GP",
        "purchase_id": "m1_14_172_premium",
        "package_name": "com.hellosingles.hellocowboys",
        "purchase_token": "dlpjpcgmhpgnpigfnnionmka.AO-J1Ox-ylRzN4odIalERHQDclB2KA6Fbyzp_Os0T6h1FBAtqd1IsgiuzuvBWI5nKPSpmzRdi3JA-fH6jtO2JdCq5KimYzRa4eWoj50MC1Txk4hXyYcq4Lk4TNLNym-1Tqy2AxRZKXTk",
        "merchant_order_number": "0.0",
        "purchase_type": "0"
    },
    "token": "dating-frontend-m1@staging",
    "password": "26ff2170e0034d29d702c65d888aaec1"
}

Example Response:

{
    "code": "700",
    "status": "Action completed successfully",
    "command": "transaction.finish",
    "result": {
        "success": true,
        "payment_tran_id": 112195,
        "items_data": [
            {
                "tracking_item": "172",
                "item_id": "38251"
            }
        ],
        "sub_ids": [
            "14390"
        ]
    },
    "request": {
        "command": "transaction.finish",
        "data": {
            "hash": "148069xxxxxx3599bd6a0e891c4bdadc",
            "pp_type": "GP",
            "purchase_id": "m1_14_172_premium",
            "package_name": "com.hellosingles.hellocowboys",
            "purchase_token": "dlpjpcgmhpgnpigfnnionmka.AO-J1Ox-ylRzN4odIalERHQDclB2KA6Fbyzp_Os0T6h1FBAtqd1IsgiuzuvBWI5nKPSpmzRdi3JA-fH6jtO2JdCq5KimYzRa4eWoj50MC1Txk4hXyYcq4Lk4TNLNym-1Tqy2AxRZKXTk",
            "merchant_order_number": "0.0",
            "purchase_type": "0"
        },
        "token": "dating-frontend-m1@staging",
        "password": "26ff2170e0034d29d702c65d888aaec1"
    },
    "ts": 1480696496,
    "origin_ip": "69.61.28.237",
    "errors": null
}
Ios
NameTypeRequiredDescriptionExample
hash(string)yesHash string of order that is related to transaction1480691234563599bd6a0e891c4bdadc
pp_type(string)yesPayment processor typeGP,IO
purchase_id(string)yesName of the payment service as created on Apple's service. Should be created so it has meaning for server app. It's just name for payment app that will be used in payment checksm1_14_123_premium
receipt-data(string)yesReceipt data - appStoreReceipthuge base 64 encoded string
Return
NameTypeDescription
resultarrayResult array with data
NameTypeDescription
successBoolWhether check was successful or not
payment_tran_idintTransaction ID on payment app
items_dataarrayItem IDs on server app and payment app
NameTypeDescription
tracking_itemintItem ID on server app(client)
item_idintItem ID on payment app
NameTypeDescription
sub_idarrayArray of created Subscription IDs(int)
previous_item_idIntNot in use. Used for non-renewing subscriptions*
expire_tsIntNot in use. Used for non-renewing subscriptions*

* Currently only renewing subscriptions are supported

Example Request:

{
    "command": "transaction.finish",
    "data": {
        "hash": "148070xxxxxxxx2201daac1c8c558f74",
        "pp_type": "IO",
        "purchase_id": "m1_14_175_premium",
        "receipt-data": "xxx"
    },
    "token": "dating-frontend-m1@staging",
    "password": "26ff2170e0034d29d702c65d888aaec1"
}

Example Response:

{
    "code": "700",
    "status": "Action completed successfully",
    "command": "transaction.finish",
    "result": {
        "success": true,
        "payment_tran_id": 112196,
        "items_data": [
            {
                "tracking_item": "175",
                "item_id": "38252"
            }
        ],
        "sub_ids": [
            "14391"
        ],
        "previous_item_id": null,
        "expire_ts": 1480790362
    },
    "request": {
        "command": "transaction.finish",
        "data": {
            "hash": "148070xxxxxxxx2201daac1c8c558f74",
            "pp_type": "IO",
            "purchase_id": "m1_14_175_premium",
            "receipt-data": "xxx"
        },
        "token": "dating-frontend-m1@staging",
        "password": "26ff2170e0034d29d702c65d888aaec1"
    },
    "ts": 1480703962,
    "origin_ip": "69.61.28.237",
    "errors": null
}
Last Author
asimic
Last Edited
Aug 2 2017, 19:03

Event Timeline

asimic edited the content of this document. (Show Details)
boris shifted this object from the Restricted Space space to the S6 Everyone space.Aug 8 2018, 08:08