diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..ee9a439
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+github: defold
+patreon: Defold
+custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NBNBHTUW4GS4C']
diff --git a/.github/workflows/trigger-site-rebuild.yml b/.github/workflows/trigger-site-rebuild.yml
new file mode 100644
index 0000000..d095ee2
--- /dev/null
+++ b/.github/workflows/trigger-site-rebuild.yml
@@ -0,0 +1,19 @@
+name: Trigger site rebuild
+
+on: [push]
+
+jobs:
+ site-rebuild:
+ runs-on: ubuntu-latest
+
+ steps: [
+ {
+ name: 'Repository dispatch',
+ uses: defold/repository-dispatch@1.2.1,
+ with: {
+ repo: 'defold/defold.github.io',
+ token: '${{ secrets.SERVICES_GITHUB_TOKEN }}',
+ user: 'services@defold.se',
+ action: 'extension-iap'
+ }
+ }]
diff --git a/README.md b/README.md
index 1826ae6..503fbff 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,4 @@
Defold [native extension](https://www.defold.com/manuals/extensions/) which provides access to In-app purchase functionality on iOS, Android (Google Play and Amazon) and Facebook Canvas platforms.
-## API and installation
-[API and setup instructions](https://defold.github.io/extension-iap).
-
-## Manual
-[The manual](https://www.defold.com/manuals/iap/) is available on the official Defold site.
+[Manual, API and setup instructions](https://www.defold.com/extension-iap/) is available on the official Defold site.
diff --git a/docs/_config.yml b/docs/_config.yml
deleted file mode 100644
index 2ca3fd3..0000000
--- a/docs/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-title: "extension-iap"
diff --git a/docs/_data/api.yml b/docs/_data/api.yml
deleted file mode 100644
index 5be1dc8..0000000
--- a/docs/_data/api.yml
+++ /dev/null
@@ -1,317 +0,0 @@
-- name: iap
- type: table
- desc: Functions and constants for doing in-app purchases. Supported on iOS, Android (Google Play and Amazon)
- and Facebook Canvas platforms.
- [icon:ios] [icon:googleplay] [icon:amazon] [icon:facebook]
- members:
-
-#*****************************************************************************************************
-
- - name: buy
- type: function
- desc: Sets the listener function for In-app purchase events.
- parameters:
- - name: id
- type: string
- desc: product to buy
-
- - name: options
- type: table
- desc: optional parameters as properties. The following parameters can be set
- members:
- - name: request_id
- type: string
- desc: Facebook only. [icon:facebook] Optional custom unique request id to
- set for this transaction. The id becomes attached to the payment within the Graph API.
-
- examples:
- - desc: |-
- ```lua
- local function iap_listener(self, transaction, error)
- if error == nil then
- -- purchase is successful.
- print(transaction.date)
- -- required if auto finish transactions is disabled in project settings
- if (transaction.state == iap.TRANS_STATE_PURCHASED) then
- -- do server-side verification of purchase here..
- iap.finish(transaction)
- end
- else
- print(error.error, error.reason)
- end
- end
-
- function init(self)
- iap.set_listener(iap_listener)
- iap.buy("my_iap")
- end
- ```
-
-#*****************************************************************************************************
-
- - name: finish
- type: function
- desc: Explicitly finish a product transaction.
- [icon:attention] Calling iap.finish is required on a successful transaction
- if `auto_finish_transactions` is disabled in project settings. Calling this function
- with `auto_finish_transactions` set will be ignored and a warning is printed.
- The `transaction.state` field must equal `iap.TRANS_STATE_PURCHASED`.
- parameters:
- - name: transaction
- type: table
- desc: transaction table parameter as supplied in listener callback
-
-#*****************************************************************************************************
-
- - name: acknowledge
- type: function
- desc: Acknowledges a product transaction but does not consume it.
- [icon:attention] [icon:googleplay] Calling iap.acknowledge is required on a successful transaction
- on Google Play unless iap.finish is called.
- The `transaction.state` field must equal `iap.TRANS_STATE_PURCHASED`.
- parameters:
- - name: transaction
- type: table
- desc: transaction table parameter as supplied in listener callback
-
-#*****************************************************************************************************
-
- - name: get_provider_id
- type: function
- desc: Get current iap provider
- returns:
- - name: provider_id
- type: constant
- desc: one of the following values
-
- - `iap.PROVIDER_ID_GOOGLE`
-
- - `iap.PROVIDER_ID_AMAZON`
-
- - `iap.PROVIDER_ID_APPLE`
-
- - `iap.PROVIDER_ID_FACEBOOK`
-
-#*****************************************************************************************************
-
- - name: list
- type: function
- desc: Get a list of all avaliable iap products.
- parameters:
- - name: ids
- type: table
- desc: table (array) of identifiers to get products from
-
- - name: callback
- type: function
- desc: result callback taking the following parameters
- parameters:
- - name: self
- type: object
- desc: The current object.
-
- - name: products
- type: table
- desc: a table describing the available iap products.
- members:
- - name: ident
- type: string
- desc: The product identifier.
-
- - name: title
- type: string
- desc: The product title.
-
- - name: description
- type: string
- desc: The product description.
-
- - name: price
- type: number
- desc: The price of the product.
-
- - name: price_string
- type: string
- desc: The price of the product, as a formatted string (amount and currency symbol).
-
- - name: currency_code
- type: string
- desc: The currency code. On Google Play, this reflects the merchant's locale, instead of the user's.
- [icon:ios] [icon:googleplay] [icon:facebook]
-
- - name: error
- type: table
- desc: a table containing error information. `nil` if there is no error. - `error` (the error message)
-
- examples:
- - desc: |-
- ```lua
- local function iap_callback(self, products, error)
- if error == nil then
- for k,p in pairs(products) do
- -- present the product
- print(p.title)
- print(p.description)
- end
- else
- print(error.error)
- end
- end
-
- function init(self)
- iap.list({"my_iap"}, iap_callback)
- end
- ```
-
-#*****************************************************************************************************
-
- - name: restore
- type: function
- desc: Restore previously purchased products.
- returns:
- - name: success
- type: boolean
- desc: value is `true` if current store supports handling
- restored transactions, otherwise `false`.
-
-#*****************************************************************************************************
-
- - name: process_pending_transactions
- type: function
- desc: Process transactions still unprocessed from previous session if any. Transactions will be
- processed with callback function set with `set_listener` function
-
-#*****************************************************************************************************
-
- - name: set_listener
- type: function
- desc: Set the callback function to receive purchase transaction events.
- parameters:
- - name: listener
- type: function
- desc: listener callback function. Pass an empty function if you no longer wish to receive callbacks.
- parameters:
- - name: self
- type: object
- desc: The current object.
-
- - name: transaction
- type: table
- desc: a table describing the transaction.
- members:
- - name: ident
- type: string
- desc: The product identifier.
-
- - name: state
- type: string
- desc: The transaction state. One of the following
-
- - `iap.TRANS_STATE_FAILED`
-
- - `iap.TRANS_STATE_PURCHASED`
-
- - `iap.TRANS_STATE_PURCHASING`
-
- - `iap.TRANS_STATE_RESTORED`
-
- - `iap.TRANS_STATE_UNVERIFIED`
-
- - name: date
- type: string
- desc: The date and time for the transaction.
-
- - name: trans_ident
- type: string
- desc: The transaction identifier. This field is only set when `state` is
- `TRANS_STATE_RESTORED`, `TRANS_STATE_UNVERIFIED` or `TRANS_STATE_PURCHASED`.
-
- - name: receipt
- type: string
- desc: The transaction receipt. This field is only set when `state` is `TRANS_STATE_PURCHASED` or `TRANS_STATE_UNVERIFIED`.
-
- - name: original_trans
- type: string
- desc: Apple only[icon:apple]. The original transaction. This field is only set when `state` is `TRANS_STATE_RESTORED`.
-
- - name: signature
- type: string
- desc: Google Play only[icon:googleplay]. A string containing the signature of the purchase data that was signed with the private key of the developer.
-
- - name: request_id
- type: string
- desc: Facebook only[icon:facebook]. This field is set to the optional custom unique request id `request_id` if set in the `iap.buy()` call parameters.
-
- - name: user_id
- type: string
- desc: Amazon Pay only[icon:amazon]. The user ID.
-
- - name: is_sandbox_mode
- type: boolean
- desc: Amazon Pay only[icon:amazon]. If `true`, the SDK is running in Sandbox mode.
- This only allows interactions with the Amazon AppTester. Use this mode only for testing locally.
-
- - name: cancel_date
- type: string
- desc: Amazon Pay only[icon:amazon]. The cancel date for the purchase. This field is only set if the purchase is canceled.
-
- - name: canceled
- type: string
- desc: Amazon Pay only[icon:amazon]. Is set to `true` if the receipt was canceled or has expired; otherwise `false`.
-
- - name: error
- type: table
- desc: a table containing error information. `nil` if there is no error. `error` - the error message.
- `reason` - the reason for the error, value can be one of the following constants
-
- - `iap.REASON_UNSPECIFIED`
-
- - `iap.REASON_USER_CANCELED`
-
-#*****************************************************************************************************
-
- - name: PROVIDER_ID_AMAZON
- type: number
- desc: provider id for Amazon
-
- - name: PROVIDER_ID_APPLE
- type: number
- desc: provider id for Apple
-
- - name: PROVIDER_ID_FACEBOOK
- type: number
- desc: provider id for Facebook
-
- - name: PROVIDER_ID_GOOGLE
- type: number
- desc: iap provider id for Google
-
- - name: REASON_UNSPECIFIED
- type: number
- desc: unspecified error reason
-
- - name: REASON_USER_CANCELED
- type: number
- desc: user canceled reason
-
- - name: TRANS_STATE_FAILED
- type: number
- desc: transaction failed state
-
- - name: TRANS_STATE_PURCHASED
- type: number
- desc: transaction purchased state
-
- - name: TRANS_STATE_PURCHASING
- type: number
- desc: transaction purchasing state
- This is an intermediate mode followed by TRANS_STATE_PURCHASED. Store provider support dependent.
-
- - name: TRANS_STATE_RESTORED
- type: number
- desc: transaction restored state
- This is only available on store providers supporting restoring purchases.
-
- - name: TRANS_STATE_UNVERIFIED
- type: number
- desc: transaction unverified state, requires verification of purchase
diff --git a/docs/_includes/api_ref.md b/docs/_includes/api_ref.md
deleted file mode 100644
index dcce1ff..0000000
--- a/docs/_includes/api_ref.md
+++ /dev/null
@@ -1,112 +0,0 @@
-## Modules
-{% for item in site.data.api %}
-### {{ item.name }}
-{{ item.desc }}
-{% endfor %}
-
-## Enums
-
-
-{% for module in site.data.api %}
- {% for item in module.members %}
- {% if item.type contains 'number' %}
-
-
-
diff --git a/docs/alpha_testers.png b/docs/alpha_testers.png
new file mode 100644
index 0000000..1911409
Binary files /dev/null and b/docs/alpha_testers.png differ
diff --git a/docs/android_purchase.png b/docs/android_purchase.png
new file mode 100644
index 0000000..e5df759
Binary files /dev/null and b/docs/android_purchase.png differ
diff --git a/docs/css/fonts.css b/docs/css/fonts.css
deleted file mode 100644
index bdf2563..0000000
--- a/docs/css/fonts.css
+++ /dev/null
@@ -1,55 +0,0 @@
-@font-face {
- font-family: 'Noto Sans';
- font-weight: 400;
- font-style: normal;
- src: url('fonts/Noto-Sans-regular/Noto-Sans-regular.eot');
- src: url('fonts/Noto-Sans-regular/Noto-Sans-regular.eot?#iefix') format('embedded-opentype'),
- local('Noto Sans'),
- local('Noto-Sans-regular'),
- url('fonts/Noto-Sans-regular/Noto-Sans-regular.woff2') format('woff2'),
- url('fonts/Noto-Sans-regular/Noto-Sans-regular.woff') format('woff'),
- url('fonts/Noto-Sans-regular/Noto-Sans-regular.ttf') format('truetype'),
- url('fonts/Noto-Sans-regular/Noto-Sans-regular.svg#NotoSans') format('svg');
-}
-
-@font-face {
- font-family: 'Noto Sans';
- font-weight: 700;
- font-style: normal;
- src: url('fonts/Noto-Sans-700/Noto-Sans-700.eot');
- src: url('fonts/Noto-Sans-700/Noto-Sans-700.eot?#iefix') format('embedded-opentype'),
- local('Noto Sans Bold'),
- local('Noto-Sans-700'),
- url('fonts/Noto-Sans-700/Noto-Sans-700.woff2') format('woff2'),
- url('fonts/Noto-Sans-700/Noto-Sans-700.woff') format('woff'),
- url('fonts/Noto-Sans-700/Noto-Sans-700.ttf') format('truetype'),
- url('fonts/Noto-Sans-700/Noto-Sans-700.svg#NotoSans') format('svg');
-}
-
-@font-face {
- font-family: 'Noto Sans';
- font-weight: 400;
- font-style: italic;
- src: url('fonts/Noto-Sans-italic/Noto-Sans-italic.eot');
- src: url('fonts/Noto-Sans-italic/Noto-Sans-italic.eot?#iefix') format('embedded-opentype'),
- local('Noto Sans Italic'),
- local('Noto-Sans-italic'),
- url('fonts/Noto-Sans-italic/Noto-Sans-italic.woff2') format('woff2'),
- url('fonts/Noto-Sans-italic/Noto-Sans-italic.woff') format('woff'),
- url('fonts/Noto-Sans-italic/Noto-Sans-italic.ttf') format('truetype'),
- url('fonts/Noto-Sans-italic/Noto-Sans-italic.svg#NotoSans') format('svg');
-}
-
-@font-face {
- font-family: 'Noto Sans';
- font-weight: 700;
- font-style: italic;
- src: url('fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot');
- src: url('fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot?#iefix') format('embedded-opentype'),
- local('Noto Sans Bold Italic'),
- local('Noto-Sans-700italic'),
- url('fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff2') format('woff2'),
- url('fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff') format('woff'),
- url('fonts/Noto-Sans-700italic/Noto-Sans-700italic.ttf') format('truetype'),
- url('fonts/Noto-Sans-700italic/Noto-Sans-700italic.svg#NotoSans') format('svg');
-}
diff --git a/docs/css/rouge.css b/docs/css/rouge.css
deleted file mode 100644
index daf76ad..0000000
--- a/docs/css/rouge.css
+++ /dev/null
@@ -1,209 +0,0 @@
-.highlight table td { padding: 5px; }
-.highlight table pre { margin: 0; }
-.highlight .cm {
- color: #999988;
- font-style: italic;
-}
-.highlight .cp {
- color: #999999;
- font-weight: bold;
-}
-.highlight .c1 {
- color: #999988;
- font-style: italic;
-}
-.highlight .cs {
- color: #999999;
- font-weight: bold;
- font-style: italic;
-}
-.highlight .c, .highlight .cd {
- color: #999988;
- font-style: italic;
-}
-.highlight .err {
- color: #a61717;
- background-color: #e3d2d2;
-}
-.highlight .gd {
- color: #000000;
- background-color: #ffdddd;
-}
-.highlight .ge {
- color: #000000;
- font-style: italic;
-}
-.highlight .gr {
- color: #aa0000;
-}
-.highlight .gh {
- color: #999999;
-}
-.highlight .gi {
- color: #000000;
- background-color: #ddffdd;
-}
-.highlight .go {
- color: #888888;
-}
-.highlight .gp {
- color: #555555;
-}
-.highlight .gs {
- font-weight: bold;
-}
-.highlight .gu {
- color: #aaaaaa;
-}
-.highlight .gt {
- color: #aa0000;
-}
-.highlight .kc {
- color: #000000;
- font-weight: bold;
-}
-.highlight .kd {
- color: #000000;
- font-weight: bold;
-}
-.highlight .kn {
- color: #000000;
- font-weight: bold;
-}
-.highlight .kp {
- color: #000000;
- font-weight: bold;
-}
-.highlight .kr {
- color: #000000;
- font-weight: bold;
-}
-.highlight .kt {
- color: #445588;
- font-weight: bold;
-}
-.highlight .k, .highlight .kv {
- color: #000000;
- font-weight: bold;
-}
-.highlight .mf {
- color: #009999;
-}
-.highlight .mh {
- color: #009999;
-}
-.highlight .il {
- color: #009999;
-}
-.highlight .mi {
- color: #009999;
-}
-.highlight .mo {
- color: #009999;
-}
-.highlight .m, .highlight .mb, .highlight .mx {
- color: #009999;
-}
-.highlight .sb {
- color: #d14;
-}
-.highlight .sc {
- color: #d14;
-}
-.highlight .sd {
- color: #d14;
-}
-.highlight .s2 {
- color: #d14;
-}
-.highlight .se {
- color: #d14;
-}
-.highlight .sh {
- color: #d14;
-}
-.highlight .si {
- color: #d14;
-}
-.highlight .sx {
- color: #d14;
-}
-.highlight .sr {
- color: #009926;
-}
-.highlight .s1 {
- color: #d14;
-}
-.highlight .ss {
- color: #990073;
-}
-.highlight .s {
- color: #d14;
-}
-.highlight .na {
- color: #008080;
-}
-.highlight .bp {
- color: #999999;
-}
-.highlight .nb {
- color: #0086B3;
-}
-.highlight .nc {
- color: #445588;
- font-weight: bold;
-}
-.highlight .no {
- color: #008080;
-}
-.highlight .nd {
- color: #3c5d5d;
- font-weight: bold;
-}
-.highlight .ni {
- color: #800080;
-}
-.highlight .ne {
- color: #990000;
- font-weight: bold;
-}
-.highlight .nf {
- color: #990000;
- font-weight: bold;
-}
-.highlight .nl {
- color: #990000;
- font-weight: bold;
-}
-.highlight .nn {
- color: #555555;
-}
-.highlight .nt {
- color: #000080;
-}
-.highlight .vc {
- color: #008080;
-}
-.highlight .vg {
- color: #008080;
-}
-.highlight .vi {
- color: #008080;
-}
-.highlight .nv {
- color: #008080;
-}
-.highlight .ow {
- color: #000000;
- font-weight: bold;
-}
-.highlight .o {
- color: #000000;
- font-weight: bold;
-}
-.highlight .w {
- color: #bbbbbb;
-}
-.highlight {
- background-color: #f8f8f8;
-}
diff --git a/docs/css/style.css b/docs/css/style.css
deleted file mode 100644
index 52b4ab0..0000000
--- a/docs/css/style.css
+++ /dev/null
@@ -1,210 +0,0 @@
-body {
- background-color: #fff;
- padding:50px;
- font: 14px/1.5 "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
- color:#727272;
- font-weight:400;
-}
-
-h1, h2, h3, h4, h5, h6 {
- color:#222;
- margin: 20px 0 20px;
-}
-
-p, ul, ol, table, pre, dl {
- margin: 20px 0 20px;
-}
-
-h1, h2, h3 {
- line-height:1.1;
-}
-
-h1 {
- font-size:28px;
-}
-
-h2 {
- color:#393939;
-}
-
-h3, h4, h5, h6 {
- color:#494949;
-}
-
-.function-header {
- margin-top: 60px;
-}
-
-a {
- color:#267CB9;
- text-decoration:none;
-}
-
-a:hover, a:focus {
- color:#069;
-}
-
-a small {
- font-size:11px;
- color:#777;
- margin-top:-0.3em;
- display:block;
-}
-
-a:hover small {
- color:#777;
-}
-
-.wrapper {
- width:860px;
- margin:0 auto;
-}
-
-.function-wrap {
- border-left: 6px solid #eee;
- padding-left: 10px;
-}
-
-blockquote {
- border-left:1px solid #e5e5e5;
- margin:0;
- padding:0 0 0 20px;
- font-style:italic;
-}
-
-code, pre {
- font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal, Consolas, Liberation Mono, DejaVu Sans Mono, Courier New, monospace;
- color:#333;
-}
-
-table code, .inline-code-block {
- background-color: #eee;
- padding: 1px 4px;
- margin: 2px;
- border-radius: 4px;
- display: inline-block;
-}
-
-pre {
- padding:8px 15px;
- background: #f8f8f8;
- border-radius:5px;
- border:1px solid #e5e5e5;
- overflow-x: auto;
-}
-
-table {
- width:100%;
- border-collapse:collapse;
-}
-
-th, td {
- text-align:left;
- vertical-align: top;
- padding:5px 10px;
- border-bottom:1px solid #e5e5e5;
-}
-
-dt {
- color:#444;
- font-weight:700;
-}
-
-th {
- color:#444;
-}
-
-img {
- max-width:100%;
-}
-
-header {
- -webkit-font-smoothing:subpixel-antialiased;
-}
-
-strong {
- color:#222;
- font-weight:700;
-}
-
-section {
- padding-bottom:50px;
-}
-
-small {
- font-size:11px;
-}
-
-hr {
- border:0;
- background:#e5e5e5;
- height:1px;
- margin:0 0 20px;
-}
-
-@media print, screen and (max-width: 960px) {
-
- div.wrapper {
- width:auto;
- margin:0;
- }
-
- header, section, footer {
- float:none;
- position:static;
- width:auto;
- }
-
- header {
-
- }
-
- section {
- border:1px solid #e5e5e5;
- border-width:1px 0;
- padding:20px 0;
- margin:0 0 20px;
- }
-
- header a small {
- display:inline;
- }
-
- header ul {
- position:absolute;
- right:50px;
- top:52px;
- }
-}
-
-@media print, screen and (max-width: 720px) {
- body {
- word-wrap:break-word;
- }
-
- header {
- padding:0;
- }
-
- header ul, header p.view {
- position:static;
- }
-
- pre, code {
- word-wrap:normal;
- }
-}
-
-@media print, screen and (max-width: 480px) {
- body {
- padding:15px;
- }
-}
-
-@media print {
- body {
- padding:0.4in;
- font-size:12pt;
- color:#444;
- }
-}
diff --git a/docs/fonts/Noto-Sans-700/Noto-Sans-700.eot b/docs/fonts/Noto-Sans-700/Noto-Sans-700.eot
deleted file mode 100644
index 03bf93f..0000000
Binary files a/docs/fonts/Noto-Sans-700/Noto-Sans-700.eot and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-700/Noto-Sans-700.svg b/docs/fonts/Noto-Sans-700/Noto-Sans-700.svg
deleted file mode 100644
index 925fe47..0000000
--- a/docs/fonts/Noto-Sans-700/Noto-Sans-700.svg
+++ /dev/null
@@ -1,336 +0,0 @@
-
-
-
diff --git a/docs/fonts/Noto-Sans-700/Noto-Sans-700.ttf b/docs/fonts/Noto-Sans-700/Noto-Sans-700.ttf
deleted file mode 100644
index 4599e3c..0000000
Binary files a/docs/fonts/Noto-Sans-700/Noto-Sans-700.ttf and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-700/Noto-Sans-700.woff b/docs/fonts/Noto-Sans-700/Noto-Sans-700.woff
deleted file mode 100644
index 9d0b78d..0000000
Binary files a/docs/fonts/Noto-Sans-700/Noto-Sans-700.woff and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-700/Noto-Sans-700.woff2 b/docs/fonts/Noto-Sans-700/Noto-Sans-700.woff2
deleted file mode 100644
index 55fc44b..0000000
Binary files a/docs/fonts/Noto-Sans-700/Noto-Sans-700.woff2 and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot b/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot
deleted file mode 100644
index cb97b2b..0000000
Binary files a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.svg b/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.svg
deleted file mode 100644
index abdafc0..0000000
--- a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.svg
+++ /dev/null
@@ -1,334 +0,0 @@
-
-
-
diff --git a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.ttf b/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.ttf
deleted file mode 100644
index 6640dbe..0000000
Binary files a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.ttf and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff b/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff
deleted file mode 100644
index 209739e..0000000
Binary files a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff2 b/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff2
deleted file mode 100644
index f5525aa..0000000
Binary files a/docs/fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff2 and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.eot b/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.eot
deleted file mode 100644
index a997349..0000000
Binary files a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.eot and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.svg b/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.svg
deleted file mode 100644
index dcd8fc8..0000000
--- a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.svg
+++ /dev/null
@@ -1,337 +0,0 @@
-
-
-
diff --git a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.ttf b/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.ttf
deleted file mode 100644
index 7f75a2d..0000000
Binary files a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.ttf and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.woff b/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.woff
deleted file mode 100644
index 6dce67c..0000000
Binary files a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.woff and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.woff2 b/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.woff2
deleted file mode 100644
index a9c14c4..0000000
Binary files a/docs/fonts/Noto-Sans-italic/Noto-Sans-italic.woff2 and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.eot b/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.eot
deleted file mode 100644
index 15fc8bf..0000000
Binary files a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.eot and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.svg b/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.svg
deleted file mode 100644
index bd2894d..0000000
--- a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.svg
+++ /dev/null
@@ -1,335 +0,0 @@
-
-
-
diff --git a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.ttf b/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.ttf
deleted file mode 100644
index a83bbf9..0000000
Binary files a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.ttf and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.woff b/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.woff
deleted file mode 100644
index 17c8500..0000000
Binary files a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.woff and /dev/null differ
diff --git a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.woff2 b/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.woff2
deleted file mode 100644
index a87d9cd..0000000
Binary files a/docs/fonts/Noto-Sans-regular/Noto-Sans-regular.woff2 and /dev/null differ
diff --git a/docs/google_play_products.png b/docs/google_play_products.png
new file mode 100644
index 0000000..ca750d5
Binary files /dev/null and b/docs/google_play_products.png differ
diff --git a/docs/index.md b/docs/index.md
index e7821ae..1a8f713 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,38 +1,199 @@
---
-layout: default
+title: Defold In-app purchase extension API documentation
+brief: This manual covers how to setup and use Google Play Game Services in Defold.
---
# Defold In-app purchase extension API documentation
-This extension provides functions for making in-app purchases.
+This extension provides a unified, simple to use interface to several different stores for in-app purchase:
+* Apple’s iOS Appstore - StoreKit
+* Google Play Billing 3.0
+* Amazon 'in-app billing' 2.0.61
+* Facebook Canvas 'game payments'
-## Usage
+These services gives you the opportunity to sell products as:
+
+* Standard in-app products (one time billing) of consumables or non-consumables and
+* Subscriptions (recurring, automated billing)
+
+::: important
+The current Defold interface allows full interaction with Apple's Storekit functionality. For Google Play and Facebook Canvas, the interface is identical, meaning that you can run the same code on either platform. However, some process flow might differ from platform to platform. Also note that there is currently no support for OS X purchases through the Mac Appstore.
+:::
+
+Detailed documentation from Apple, Google, Amazon and Facebook can be found here:
+
+* [In-App Purchase Programming Guide](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html).
+* [Google Play In-app Billing documentation](http://developer.android.com/google/play/billing/index.html).
+* [Amazon In-app Purchase documentation](https://developer.amazon.com/public/apis/earn/in-app-purchasing).
+* [Facebook game payments documentation](https://developers.facebook.com/docs/payments).
+
+## Installation
To use this library in your Defold project, add the following URL to your `game.project` dependencies:
- https://github.com/defold/extension-iap/archive/master.zip
+https://github.com/defold/extension-iap/archive/master.zip
We recommend using a link to a zip file of a [specific release](https://github.com/defold/extension-iap/releases).
For Facebook Canvas you also need to add the [Facebook extension as a dependency](https://github.com/defold/extension-facebook).
-## Source code
+## Testing Google Play Billing with static responses
-The source code is available on [GitHub](https://github.com/defold/extension-iap)
+On Android it is recommended that you start implementing IAP in your app by using static responses from Google Play. This enables you to verify that everything in your app works correctly before you publish the app. Four reserved product IDs exist for testing static In-app Billing responses:
+
+`android.test.purchased`
+: Google Play responds as though you successfully purchased an item. The response includes a JSON string, which contains fake purchase information (for example, a fake order ID).
+
+`android.test.canceled`
+: Google Play responds as though the purchase was canceled. This can occur when an error is encountered in the order process, such as an invalid credit card, or when you cancel a user's order before it is charged.
+
+`android.test.refunded`
+: Google Play responds as though the purchase was refunded.
+
+`android.test.item_unavailable`
+: Google Play responds as though the item being purchased was not listed in your application's product list.
-## Supported platforms
+## Setting up your app for purchases/billing
-The following platforms are supported by the extension:
+The procedure on iOS and Android is similar:
-* iOS - StoreKit
-* Google Play - Billing 3.0.0
-* Amazon - 2.0.61
-* Facebook Canvas
+1. Make sure you are a registered Apple or Google Play developer.
+2. Set up your project so it works on your target device. See the [iOS development](/manuals/ios) and [Android development](/manuals/android) guides.
+3. Set up the app for testing:
+
+ - For Android, this is done on the [Google Play Developer Console](https://play.google.com/apps/publish/).
+ - For iOS, this is done on [iTunes Connect](https://itunesconnect.apple.com/). Make sure that your App ID (created in the "Member Center" on https://developer.apple.com) has "In-App Purchase" enabled.
+
+ 
+
+4. For Google Play, you need to _upload and publish_ an alpha *.apk* file. For iTunes Connect, you should _not upload_ the development binary to iTunes Connect until the application is ready for App Review approval. If you upload a binary to iTunes Connect and it is not fully functional, Apple will likely reject it.
+
+5. Create products for your app.
+
+ 
+
+ 
+
+6. Add test users.
+ - The iTunes Connect page *Users and Roles* allow you to add users that can do test purchases in the _sandbox environment_. You should sign your app with a Developer certificate and use the sandbox account in Appstore on the test device.
+ - From the Google Play Developer Console, choose *Settings > Account Details* where you can add user emails to the License Testing section. Separate the emails by commas. This allows your testers to use test purchases that don’t actually cost real money.
+ - On Google Play, you also need to set up a Google Group for your testers. Google uses Groups to manage testers that can download your app from the Alpha and Beta stores. Click on the *Alpha Testing* tab and then *Manage list of testers* to add your Google Group as Alpha testers. The app must have passed through alpha publishing before you can see the opt-in link.
+
+
+
+The procedure on Facebook:
+
+1. Make sure you are a registered Facebook developer. Go to [Facebook for developers](https://developers.facebook.com/), "My Apps" and "Register as a developer", follow the steps.
+2. Facebook has extensive payment functionality and requires support of both synchronous and asynchronous payments. More info here [Payment overview](https://developers.facebook.com/docs/payments/overview)
+3. Set up app hosting and callback server:
+ * You will need to set up a secure canvas URL hosting your project. How this works is explained here [Games on Facebook](https://developers.facebook.com/docs/games/gamesonfacebook/hosting).
+ * The next step is to set up your callback server. Follow the steps here [Setting up your callback server](https://developers.facebook.com/docs/payments/realtimeupdates#yourcallbackserver).
+4. Set up you canvas app. Follow the steps on [Facebook Developer Dashboard](https://developers.facebook.com/quickstarts/?platform=canvas).
+5. Add test users. This is done in the "Canvas Payments" section of the app dashboard.
+6. Create products for your app [Defining products](https://developers.facebook.com/docs/payments/implementation-guide/defining-products/).
-### Differences between supported platforms
+## Asynchronous transactions
+
+The IAP API is asynchronous, meaning that after each request that your program sends to the server, the program will not halt and wait for a response. Instead, the program continues as ordinary and when the response arrives, a _callback_ function is invoked where you can react to the response data.
+
+To fetch all product information available:
+
+```lua
+local COINS_ID = "com.defold.examples.coins"
+local LOGO_ID = "com.defold.examples.logo"
+
+local function product_list(self, products, error)
+ if error == nil then
+ for i,p in pairs(products) do
+ print(p.ident)
+ print(p.title)
+ print(p.description)
+ print(p.currency_code)
+ print(p.price_string)
+ end
+ else
+ print(error.error)
+ end
+end
+
+function init(self)
+ -- Initiate a fetch of products (max 20 at a time for Google Play)
+ iap.list({ COINS_ID, LOGO_ID }, product_list)
+end
+```
+
+To perform actual transactions, first register a function that will listen to transaction results, then call the store function at the appropriate time:
+
+```lua
+local function iap_listener(self, transaction, error)
+ if error == nil then
+ if transaction.state == iap.TRANS_STATE_PURCHASING then
+ print("Purchasing...")
+ elseif transaction.state == iap.TRANS_STATE_PURCHASED then
+ print("Purchased!")
+ elseif transaction.state == iap.TRANS_STATE_UNVERIFIED then
+ print("Unverified!")
+ elseif transaction.state == iap.TRANS_STATE_FAILED then
+ print("Failed!")
+ elseif transaction.state == iap.TRANS_STATE_RESTORED then
+ print("Restored")
+ end
+ else
+ print(error.error)
+ end
+end
+
+function on_message(self, message_id, message, sender)
+ ...
+ -- Register the function that will listen to IAP transactions.
+ iap.set_listener(iap_listener)
+ -- Initiate a purchase of a coin...
+ iap.buy(COINS_ID)
+ ...
+end
+```
+
+The device operating system will automatically show a pop-up window allowing the user to go through with the purchase. The interface clearly indicates when you are running in the test/sandbox environment.
+
+
+
+
+
+
+
+
+## Synchronous payments
+
+Most payment providers only supports synchronous payments. This means that the client (your application) will receive a notification when the payment is complete, TRANS_STATE_PURCHASED. This is the final state of the payment, meaning no more callbacks will be done on this transaction.
+
+
+## Asynchronous payments
+
+Some payment providers require supporting asynchronous payments. This means that the client (your application) will only receive a notification when the payment is initiated. In order to verify completion of payment, further communication needs to be done between the developer server (or client) and the payment provider in order to verify.
+In the case of an initiated asynchronous payment the IAP listener will receive the state TRANS_STATE_UNVERIFIED to indicate this (as opposed to TRANS_STATE_PURCHASED). This is the final state of the payment, meaning no more callbacks will be done on this transaction.
+
+
+## Purchase fulfilment
+
+In order to complete a purchase from a payment provider, the application needs to signal a purchase fulfilment to the provider telling the provider the purchase has gone through (for example by developer server-side verification).
+IAP supports auto-completion, where fulfilment is automatically signalled to the provider when a purchase is complete (this is the default behavior). You can also disable auto-completion in the game project settings. You are then required to call `iap.finish()` when the transaction is complete, which will signal purchase fulfilment to the provider.
+
+
+### Consumable vs non-consumable products
+The Google Play store does only support consumable products. If you need non-consumable products it is recommended to use manual fulfillment of purchases and never finish purchases for products that should be non-consumable. As long as a purchase isn't finished it will be returned as an active purchase when `iap.set_listener()` is called.
+
+The Apple App Store supports non-consumable products which means that you need to finish all purchases when you provide products to your users. You can do it automatically by keeping the default behavior in the game project settings or manually (if you want to do that after server validation, for example) using `iap.finish()`.
+
+
+## Transaction receipt
+
+The receipt is a signed chunk of data that can be sent to the App Store to verify that the payment was successfully processed. This is most useful when designing a store that uses a separate server to verify that payment was processed.
+
+
+## Differences between supported platforms
Amazon supports two different product types: subscriptions and consumable products.
@@ -44,8 +205,32 @@ Calls to `iap.buy()` and `iap.set_listener()` will return all non-finished purch
The concept of restoring purchases does not exist on Google Play/Amazon. Calls to `iap.restore()` on iOS will return all purchased products (and have product state set to TRANS_STATE_RESTORED). Calls to `iap.restore()` on Google Play will return all non-finished purchases (and have product state set to TRANS_STATE_PURCHASED).
----
-# API reference
+## Troubleshooting
-{% include api_ref.md %}
+Android `iap.list()` returns "failed to fetch product"
+: You need to upload and publish an *.apk* on the alpha or beta channels on the Google Play Developer Console. Also make sure that the _time and date_ on your device is correct.
+
+Android (Google Play) `iap.list()` never returns more than 20 products
+: Google has an [limit of 20 products per request](https://github.com/android/play-billing-samples/blob/7a94c6905a9c125518354c216b5c3094fde47ce1/TrivialDrive/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl#L62). The solution is to make multiple calls to `iap.list()` and combine the results if the number of products exceeds 20.
+
+iOS `iap.list()` returns nothing
+: Make sure that you’ve requested an iOS Paid Applications account, and all proper documentation has been filed. Without proper authorization, your iOS app purchasing (even test purchases) will not work.
+
+ Check that the AppId you have on the "Member Center" has in-app purchases activated and that you are signing your app (or the dev-app) with a provisioning profile that is up to date with the AppId (check the "Enabled Services:" field in the provisioning profile details in the "Certificates, Identifiers & Profiles" area of "Member Center")
+
+ Wait. It can take a few hours for the In-App product IDs to propagate to the Sandbox environment.
+
+iOS `iap.list()` fails logging error "Unexpected callback set"
+: `iap.list()` does not support nested calls. Calling `iap.list()` from an `iap.list()` callback function will be ignored, with the engine logging this error.
+
+On iOS, the "price_string" field contains '~' characters
+: The '~' characters are placeholders where no matching character could be found in the font file. The "price_string" field returned in the product list when using `iap.list()` is formatted with a _non breaking space_ (`\u00a0`) between the value and the currency denominator. If you render this string in the GUI, you need to add the character to the font's *extra_characters* field. On Mac OS X you can type non breaking spaces by pressing Option + SPACE. See http://en.wikipedia.org/wiki/Non-breaking_space for more information.
+
+
+## Source code
+
+The source code is available on [GitHub](https://github.com/defold/extension-iap)
+
+
+## API reference
diff --git a/docs/ios_confirm_purchase.png b/docs/ios_confirm_purchase.png
new file mode 100644
index 0000000..f5901a7
Binary files /dev/null and b/docs/ios_confirm_purchase.png differ
diff --git a/docs/ios_purchase_done.png b/docs/ios_purchase_done.png
new file mode 100644
index 0000000..d81d777
Binary files /dev/null and b/docs/ios_purchase_done.png differ
diff --git a/docs/itunes_connect_google_play.png b/docs/itunes_connect_google_play.png
new file mode 100644
index 0000000..d9dfd04
Binary files /dev/null and b/docs/itunes_connect_google_play.png differ
diff --git a/docs/itunes_products.png b/docs/itunes_products.png
new file mode 100644
index 0000000..c7ca2f3
Binary files /dev/null and b/docs/itunes_products.png differ
diff --git a/game.project b/game.project
index 71d0201..5a2b8e8 100644
--- a/game.project
+++ b/game.project
@@ -11,7 +11,8 @@ height = 1136
[android]
input_method = HiddenInputField
package = com.defold.extension.iap
-version_code = 5
+version_code = 7
+target_sdk_version = 29
[project]
title = extension-iap