Merge pull request #145 from simple-login/macapp
Support Macapp payment
This commit is contained in:
commit
c4e20c2f9c
|
@ -1144,6 +1144,7 @@ Process payment receipt
|
||||||
Input:
|
Input:
|
||||||
- `Authentication` in header: the api key
|
- `Authentication` in header: the api key
|
||||||
- `receipt_data` in body: the receipt_data base64Encoded returned by StoreKit, i.e. `rawReceiptData.base64EncodedString`
|
- `receipt_data` in body: the receipt_data base64Encoded returned by StoreKit, i.e. `rawReceiptData.base64EncodedString`
|
||||||
|
- (optional) `is_macapp` in body: if this field is present, the request is sent from the MacApp (Safari Extension) and not iOS app.
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
200 if user is upgraded successfully
|
200 if user is upgraded successfully
|
||||||
|
|
|
@ -8,7 +8,7 @@ from flask import request
|
||||||
from flask_cors import cross_origin
|
from flask_cors import cross_origin
|
||||||
|
|
||||||
from app.api.base import api_bp, require_api_auth
|
from app.api.base import api_bp, require_api_auth
|
||||||
from app.config import APPLE_API_SECRET
|
from app.config import APPLE_API_SECRET, MACAPP_APPLE_API_SECRET
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
from app.models import PlanEnum, AppleSubscription
|
from app.models import PlanEnum, AppleSubscription
|
||||||
|
@ -16,6 +16,9 @@ from app.models import PlanEnum, AppleSubscription
|
||||||
_MONTHLY_PRODUCT_ID = "io.simplelogin.ios_app.subscription.premium.monthly"
|
_MONTHLY_PRODUCT_ID = "io.simplelogin.ios_app.subscription.premium.monthly"
|
||||||
_YEARLY_PRODUCT_ID = "io.simplelogin.ios_app.subscription.premium.yearly"
|
_YEARLY_PRODUCT_ID = "io.simplelogin.ios_app.subscription.premium.yearly"
|
||||||
|
|
||||||
|
_MACAPP_MONTHLY_PRODUCT_ID = "io.simplelogin.macapp.subscription.premium.monthly"
|
||||||
|
_MACAPP_PRODUCT_ID = "io.simplelogin.macapp.subscription.premium.yearly"
|
||||||
|
|
||||||
# Apple API URL
|
# Apple API URL
|
||||||
_SANDBOX_URL = "https://sandbox.itunes.apple.com/verifyReceipt"
|
_SANDBOX_URL = "https://sandbox.itunes.apple.com/verifyReceipt"
|
||||||
_PROD_URL = "https://buy.itunes.apple.com/verifyReceipt"
|
_PROD_URL = "https://buy.itunes.apple.com/verifyReceipt"
|
||||||
|
@ -29,15 +32,23 @@ def apple_process_payment():
|
||||||
Process payment
|
Process payment
|
||||||
Input:
|
Input:
|
||||||
receipt_data: in body
|
receipt_data: in body
|
||||||
|
(optional) is_macapp: in body
|
||||||
Output:
|
Output:
|
||||||
200 of the payment is successful, i.e. user is upgraded to premium
|
200 of the payment is successful, i.e. user is upgraded to premium
|
||||||
|
|
||||||
"""
|
"""
|
||||||
LOG.debug("request for /apple/process_payment")
|
LOG.debug("request for /apple/process_payment")
|
||||||
user = g.user
|
user = g.user
|
||||||
receipt_data = request.get_json().get("receipt_data")
|
data = request.get_json()
|
||||||
|
receipt_data = data.get("receipt_data")
|
||||||
|
is_macapp = "is_macapp" in data
|
||||||
|
|
||||||
apple_sub = verify_receipt(receipt_data, user)
|
if is_macapp:
|
||||||
|
password = MACAPP_APPLE_API_SECRET
|
||||||
|
else:
|
||||||
|
password = APPLE_API_SECRET
|
||||||
|
|
||||||
|
apple_sub = verify_receipt(receipt_data, user, password)
|
||||||
if apple_sub:
|
if apple_sub:
|
||||||
return jsonify(ok=True), 200
|
return jsonify(ok=True), 200
|
||||||
|
|
||||||
|
@ -249,7 +260,8 @@ def apple_update_notification():
|
||||||
expires_date = arrow.get(int(transaction["expires_date_ms"]) / 1000)
|
expires_date = arrow.get(int(transaction["expires_date_ms"]) / 1000)
|
||||||
plan = (
|
plan = (
|
||||||
PlanEnum.monthly
|
PlanEnum.monthly
|
||||||
if transaction["product_id"] == _MONTHLY_PRODUCT_ID
|
if transaction["product_id"]
|
||||||
|
in (_MONTHLY_PRODUCT_ID, _MACAPP_MONTHLY_PRODUCT_ID)
|
||||||
else PlanEnum.yearly
|
else PlanEnum.yearly
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -279,7 +291,7 @@ def apple_update_notification():
|
||||||
return jsonify(ok=False), 400
|
return jsonify(ok=False), 400
|
||||||
|
|
||||||
|
|
||||||
def verify_receipt(receipt_data, user) -> Optional[AppleSubscription]:
|
def verify_receipt(receipt_data, user, password) -> Optional[AppleSubscription]:
|
||||||
"""Call verifyReceipt endpoint and create/update AppleSubscription table
|
"""Call verifyReceipt endpoint and create/update AppleSubscription table
|
||||||
Call the production URL for verifyReceipt first,
|
Call the production URL for verifyReceipt first,
|
||||||
and proceed to verify with the sandbox URL if receive a 21007 status code.
|
and proceed to verify with the sandbox URL if receive a 21007 status code.
|
||||||
|
@ -290,15 +302,14 @@ def verify_receipt(receipt_data, user) -> Optional[AppleSubscription]:
|
||||||
"""
|
"""
|
||||||
LOG.d("start verify_receipt")
|
LOG.d("start verify_receipt")
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
_PROD_URL, json={"receipt-data": receipt_data, "password": APPLE_API_SECRET}
|
_PROD_URL, json={"receipt-data": receipt_data, "password": password}
|
||||||
)
|
)
|
||||||
|
|
||||||
if r.json() == {"status": 21007}:
|
if r.json() == {"status": 21007}:
|
||||||
# try sandbox_url
|
# try sandbox_url
|
||||||
LOG.warning("Use the sandbox url instead")
|
LOG.warning("Use the sandbox url instead")
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
_SANDBOX_URL,
|
_SANDBOX_URL, json={"receipt-data": receipt_data, "password": password},
|
||||||
json={"receipt-data": receipt_data, "password": APPLE_API_SECRET},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data = r.json()
|
data = r.json()
|
||||||
|
|
|
@ -243,4 +243,7 @@ with open(get_abs_path(DISPOSABLE_FILE_PATH), "r") as f:
|
||||||
]
|
]
|
||||||
|
|
||||||
# Used when querying info on Apple API
|
# Used when querying info on Apple API
|
||||||
|
# for iOS App
|
||||||
APPLE_API_SECRET = os.environ.get("APPLE_API_SECRET")
|
APPLE_API_SECRET = os.environ.get("APPLE_API_SECRET")
|
||||||
|
# for Mac App
|
||||||
|
MACAPP_APPLE_API_SECRET = os.environ.get("MACAPP_APPLE_API_SECRET")
|
||||||
|
|
10
cron.py
10
cron.py
|
@ -6,7 +6,12 @@ from arrow import Arrow
|
||||||
|
|
||||||
from app import s3
|
from app import s3
|
||||||
from app.api.views.apple import verify_receipt
|
from app.api.views.apple import verify_receipt
|
||||||
from app.config import IGNORED_EMAILS, ADMIN_EMAIL
|
from app.config import (
|
||||||
|
IGNORED_EMAILS,
|
||||||
|
ADMIN_EMAIL,
|
||||||
|
MACAPP_APPLE_API_SECRET,
|
||||||
|
APPLE_API_SECRET,
|
||||||
|
)
|
||||||
from app.email_utils import send_email, send_trial_end_soon_email, render
|
from app.email_utils import send_email, send_trial_end_soon_email, render
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
|
@ -114,7 +119,8 @@ def poll_apple_subscription():
|
||||||
# todo: only near the end of the subscription
|
# todo: only near the end of the subscription
|
||||||
for apple_sub in AppleSubscription.query.all():
|
for apple_sub in AppleSubscription.query.all():
|
||||||
user = apple_sub.user
|
user = apple_sub.user
|
||||||
verify_receipt(apple_sub.receipt_data, user)
|
verify_receipt(apple_sub.receipt_data, user, APPLE_API_SECRET)
|
||||||
|
verify_receipt(apple_sub.receipt_data, user, MACAPP_APPLE_API_SECRET)
|
||||||
|
|
||||||
LOG.d("Finish poll_apple_subscription")
|
LOG.d("Finish poll_apple_subscription")
|
||||||
|
|
||||||
|
|
|
@ -125,4 +125,5 @@ FACEBOOK_CLIENT_SECRET=to_fill
|
||||||
# LANDING_PAGE_URL=https://simplelogin.io
|
# LANDING_PAGE_URL=https://simplelogin.io
|
||||||
|
|
||||||
# Used when querying info on Apple API
|
# Used when querying info on Apple API
|
||||||
# APPLE_API_SECRET=secret
|
# APPLE_API_SECRET=secret
|
||||||
|
# MACAPP_APPLE_API_SECRET=secret
|
Loading…
Reference in a new issue