Skip to content

Commit

Permalink
Let there be code
Browse files Browse the repository at this point in the history
  • Loading branch information
mhemmings committed Nov 2, 2020
1 parent 3001169 commit 2b73c6a
Show file tree
Hide file tree
Showing 22 changed files with 1,040 additions and 1 deletion.
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Test

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-20.04
strategy:
matrix:
go-version: [1.13.x, 1.14.x, 1.15.x]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: go vet
run: go vet ./...
- name: go test
run: go test -v ./...
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 Mark Hemmings

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
136 changes: 135 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,135 @@
# revenuecat
# RevenueCat

[![PkgGoDev](https://pkg.go.dev/badge/pkg.go.dev/github.com/mhemmings/revenuecat)](https://pkg.go.dev/pkg.go.dev/github.com/mhemmings/revenuecat)
[![Test](https://github.com/mhemmings/revenuecat/workflows/Test/badge.svg?branch=master)](https://github.com/mhemmings/revenuecat/actions?query=workflow%3ATest)

Go package for interacting with the [RevenueCat API](https://docs.revenuecat.com/reference).

## Usage

### Example

```go
package main

import (
"fmt"

"github.com/mhemmings/revenuecat"
)

func main() {
rc := revenuecat.New("apikey")
sub, _ := rc.GetSubscriber("123")
entitled := sub.IsEntitledTo("premium")

fmt.Println("user entitled: %t", entitled)
}
```

### Documentation

For full documentation, see [pkg.go.dev/github.com/mhemmings/revenuecat](https://pkg.go.dev/github.com/mhemmings/revenuecat)


#### func (*Client) AddUserAttribution

```go
func (c *Client) AddUserAttribution(userID string, network Network, data AttributionData) error
```
AddUserAttribution attaches attribution data to a subscriber from specific
supported networks. https://docs.revenuecat.com/reference#subscribersattribution

#### func (*Client) CreatePurchase

```go
func (c *Client) CreatePurchase(userID string, receipt string, opt *CreatePurchaseOptions) (Subscriber, error)
```
CreatePurchase records a purchase for a user from iOS, Android, or Stripe and
will create a user if they don't already exist.
https://docs.revenuecat.com/reference#receipts

#### func (*Client) DeferGoogleSubscription

```go
func (c *Client) DeferGoogleSubscription(userID string, id string, nextExpiry time.Time) (Subscriber, error)
```
DeferGoogleSubscription defers the purchase of a Google Subscription to a later
date. https://docs.revenuecat.com/reference#defer-a-google-subscription

#### func (*Client) DeleteOfferingOverride

```go
func (c *Client) DeleteOfferingOverride(userID string) (Subscriber, error)
```
DeleteOfferingOverride reset the offering overrides back to the current offering
for a specific user.
https://docs.revenuecat.com/reference#delete-offering-override

#### func (*Client) DeleteSubscriber

```go
func (c *Client) DeleteSubscriber(userID string) error
```
DeleteSubscriber permanently deletes a subscriber.
https://docs.revenuecat.com/reference#subscribersapp_user_id

#### func (*Client) GetSubscriber

```go
func (c *Client) GetSubscriber(userID string) (Subscriber, error)
```
GetSubscriber gets the latest subscriber info or creates one if it doesn't
exist. https://docs.revenuecat.com/reference#subscribers

#### func (*Client) GetSubscriberWithPlatform

```go
func (c *Client) GetSubscriberWithPlatform(userID string, platform string) (Subscriber, error)
```
GetSubscriberWithPlatform gets the latest subscriber info or creates one if it
doesn't exist, updating the subscriber record's last_seen value for the platform
provided. https://docs.revenuecat.com/reference#subscribers

#### func (*Client) GrantEntitlement

```go
func (c *Client) GrantEntitlement(userID string, id string, duration Duration, startTime time.Time) (Subscriber, error)
```
GrantEntitlement grants a user a promotional entitlement.
https://docs.revenuecat.com/reference#grant-a-promotional-entitlement

#### func (*Client) OverrideOffering

```go
func (c *Client) OverrideOffering(userID string, offeringUUID string) (Subscriber, error)
```
OverrideOffering overrides the current Offering for a specific user.
https://docs.revenuecat.com/reference#override-offering

#### func (*Client) RefundGoogleSubscription

```go
func (c *Client) RefundGoogleSubscription(userID string, id string) (Subscriber, error)
```
RefundGoogleSubscription immediately revokes access to a Google Subscription and
issues a refund for the last purchase.
https://docs.revenuecat.com/reference#revoke-a-google-subscription

#### func (*Client) RevokeEntitlement

```go
func (c *Client) RevokeEntitlement(userID string, id string) (Subscriber, error)
```
RevokeEntitlement revokes all promotional entitlements for a given entitlement
identifier and app user ID.
https://docs.revenuecat.com/reference#revoke-promotional-entitlements

#### func (*Client) UpdateSubscriberAttributes

```go
func (c *Client) UpdateSubscriberAttributes(userID string, attributes map[string]SubscriberAttribute) error
```
UpdateSubscriberAttributes updates subscriber attributes for a user.
https://docs.revenuecat.com/reference#update-subscriber-attributes

38 changes: 38 additions & 0 deletions attribution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package revenuecat

// Network represents a predefined attribution channel.
type Network int

// https://docs.revenuecat.com/reference#attribution-source-network-codes
const (
AppleSearchAds Network = iota
Adjust
AppsFlyer
Branch
Tenjin
Facebook
)

// AttributionData holds the identifier value for either the App Store or Play Services.
type AttributionData struct {
IDFA string `json:"rc_idfa,omitempty"`
PlayServicesID string `json:"rc_gps_adid,omitempty"`
}

// AddUserAttribution attaches attribution data to a subscriber from specific supported networks.
// https://docs.revenuecat.com/reference#subscribersattribution
func (c *Client) AddUserAttribution(userID string, network Network, data AttributionData) error {
var resp struct {
Subscriber Subscriber `json:"subscriber"`
}

req := struct {
Data AttributionData `json:"data"`
Network Network `json:"network"`
}{
Data: data,
Network: network,
}

return c.call("POST", "subscribers/"+userID+"/attribution", req, "", &resp)
}
20 changes: 20 additions & 0 deletions attribution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package revenuecat

import (
"testing"
)

func TestAddUserAttribution(t *testing.T) {
cl := newMockClient(t, 200, nil, nil)
rc := New("apikey")
rc.http = cl

err := rc.AddUserAttribution("123", Facebook, AttributionData{IDFA: "test"})
if err != nil {
t.Errorf("error: %v", err)
}

cl.expectMethod(t, "POST")
cl.expectPath(t, "/v1/subscribers/123/attribution")
cl.expectBody(t, `{"data":{"rc_idfa":"test"},"network":5}`)
}
77 changes: 77 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package revenuecat

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)

// Client makes authorized calls to the RevenueCat API.
type Client struct {
apiKey string
apiURL string
http doer
}

type doer interface {
Do(req *http.Request) (*http.Response, error)
}

// New returns a new *Client for the provided API key.
// For more information on authentication, see https://docs.revenuecat.com/docs/authentication.
func New(apiKey string) *Client {
return &Client{
apiKey: apiKey,
apiURL: "https://api.revenuecat.com/v1/",
http: &http.Client{
// Set a long timeout here since calls to Apple are probably invloved.
Timeout: 10 * time.Second,
},
}
}

func (c *Client) call(method, path string, reqBody interface{}, platform string, respBody interface{}) error {
var reqBodyJSON io.Reader
if reqBody != nil {
js, err := json.Marshal(reqBody)
if err != nil {
return fmt.Errorf("error marshaling request body: %v", err)
}
reqBodyJSON = bytes.NewBuffer(js)
}
req, err := http.NewRequest(method, c.apiURL+path, reqBodyJSON)
if err != nil {
return fmt.Errorf("error creating request: %v", err)
}
req.Header.Add("Authorization", "Bearer "+c.apiKey)
req.Header.Add("Content-Type", "application/json")
if platform != "" {
req.Header.Add("X-Platform", "ios")
}

resp, err := c.http.Do(req)
if err != nil {
return fmt.Errorf("error making request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
var errResp Error
err = json.NewDecoder(resp.Body).Decode(&errResp)
if err != nil {
return err
}
return errResp
}
if respBody == nil {
// Expecting an empty body.
return nil
}
err = json.NewDecoder(resp.Body).Decode(respBody)
if err != nil {
return fmt.Errorf("error decoding response: %v", err)
}
return nil
}
Loading

0 comments on commit 2b73c6a

Please sign in to comment.