Skip to content

Commit

Permalink
Merge branch 'blacklightcms:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
kmikiy authored Oct 14, 2021
2 parents e9fa8bc + 0c0179f commit 0c3af72
Show file tree
Hide file tree
Showing 48 changed files with 1,495 additions and 154 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Recurly is a Go (golang) API Client for the [Recurly](https://recurly.com/) API. It is actively maintained, unit tested, and uses no external dependencies. The vast majority of the API is implemented.

Supports:
- Recurly API `v2.21`
- Recurly API `v2.27`
- Accounts
- Add Ons
- Adjustments
Expand Down
1 change: 1 addition & 0 deletions accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ type Account struct {
HasPastDueInvoice NullBool `xml:"has_past_due_invoice,omitempty"`
PreferredLocale string `xml:"preferred_locale,omitempty"`
CustomFields *CustomFields `xml:"custom_fields,omitempty"`
TransactionType string `xml:"transaction_type,omitempty"` // Create only
}

// AccountBalance is used for getting the account balance.
Expand Down
8 changes: 8 additions & 0 deletions accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ func TestAccounts_Encoding(t *testing.T) {
</account>
`),
},
{
v: recurly.Account{TransactionType: "moto"},
expected: MustCompactString(`
<account>
<transaction_type>moto</transaction_type>
</account>
`),
},
{
v: recurly.Account{FirstName: "Larry", Address: &recurly.Address{Address: "123 Main St.", City: "San Francisco", State: "CA", Zip: "94105", Country: "US"}},
expected: MustCompactString(`
Expand Down
19 changes: 18 additions & 1 deletion add_ons.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,17 @@ type AddOn struct {
TaxCode string `xml:"tax_code,omitempty"`
UnitAmountInCents UnitAmount `xml:"unit_amount_in_cents,omitempty"`
AccountingCode string `xml:"accounting_code,omitempty"`
CreatedAt NullTime `xml:"created_at,omitempty"`
ExternalSKU string `xml:"external_sku,omitempty"`
ItemState string `xml:"item_state,omitempty"`
ItemCode string `xml:"item_code,omitempty"`
TierType string `xml:"tier_type,omitempty"`
Tiers *[]Tier `xml:"tiers>tier,omitempty"`

// The following are only valid with an `Avalara for Communications` integration
AvalaraTransactionType int `xml:"avalara_transaction_type,omitempty"`
AvalaraServiceType int `xml:"avalara_service_type,omitempty"`

CreatedAt NullTime `xml:"created_at,omitempty"`
}

// UnitAmount can read or write amounts in various currencies.
Expand All @@ -73,6 +83,13 @@ func (u UnitAmount) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return nil
}

// Tier is used for Quantity Based Pricing models https://docs.recurly.com/docs/billing-models#section-quantity-based
type Tier struct {
XMLName xml.Name `xml:"tier"`
UnitAmountInCents UnitAmount `xml:"unit_amount_in_cents,omitempty"`
EndingQuantity int `xml:"ending_quantity,omitempty"`
}

var _ AddOnsService = &addOnsImpl{}

// addOnsImpl implements AddOnsService.
Expand Down
68 changes: 68 additions & 0 deletions add_ons_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,62 @@ func TestAddOns_Encoding(t *testing.T) {
</add_on>
`),
},
{
v: recurly.AddOn{ItemCode: "pink_sweaters"},
expected: MustCompactString(`
<add_on>
<item_code>pink_sweaters</item_code>
</add_on>
`),
},
{
v: recurly.AddOn{ExternalSKU: "BC-123-ABC"},
expected: MustCompactString(`
<add_on>
<external_sku>BC-123-ABC</external_sku>
</add_on>
`),
},
{
v: recurly.AddOn{ItemState: "active"},
expected: MustCompactString(`
<add_on>
<item_state>active</item_state>
</add_on>
`),
},
{
v: recurly.AddOn{AvalaraServiceType: 300, AvalaraTransactionType: 6},
expected: MustCompactString(`
<add_on>
<avalara_transaction_type>6</avalara_transaction_type>
<avalara_service_type>300</avalara_service_type>
</add_on>
`),
},
{
v: recurly.AddOn{TierType: "flat"},
expected: MustCompactString(`
<add_on>
<tier_type>flat</tier_type>
</add_on>
`),
},
{
v: recurly.AddOn{Tiers: &[]recurly.Tier{*NewTestTier()}},
expected: MustCompactString(`
<add_on>
<tiers>
<tier>
<unit_amount_in_cents>
<USD>100</USD>
</unit_amount_in_cents>
<ending_quantity>500</ending_quantity>
</tier>
</tiers>
</add_on>
`),
},
}

for i, tt := range tests {
Expand Down Expand Up @@ -331,7 +387,19 @@ func NewTestAddOn() *recurly.AddOn {
UnitAmountInCents: recurly.UnitAmount{
USD: 200,
},
TierType: "volume",
Tiers: &[]recurly.Tier{*NewTestTier()},
AccountingCode: "abc123",
CreatedAt: recurly.NewTime(MustParseTime("2011-06-28T12:34:56Z")),
}
}

func NewTestTier() *recurly.Tier {
return &recurly.Tier{
XMLName: xml.Name{Local: "tier"},
EndingQuantity: 500,
UnitAmountInCents: recurly.UnitAmount{
USD: 100,
},
}
}
68 changes: 40 additions & 28 deletions adjustments.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,47 +81,57 @@ type Adjustment struct {
TaxInCents int `xml:"tax_in_cents,omitempty"`
TotalInCents int `xml:"total_in_cents,omitempty"`
Currency string `xml:"currency"`
Taxable NullBool `xml:"taxable,omitempty"`
TaxCode string `xml:"tax_code,omitempty"`
TaxType string `xml:"tax_type,omitempty"`
TaxRegion string `xml:"tax_region,omitempty"`
TaxRate float64 `xml:"tax_rate,omitempty"`
TaxExempt NullBool `xml:"tax_exempt,omitempty"`
TaxDetails []TaxDetail `xml:"tax_details>tax_detail,omitempty"`
StartDate NullTime `xml:"start_date,omitempty"`
EndDate NullTime `xml:"end_date,omitempty"`
CreatedAt NullTime `xml:"created_at,omitempty"`
UpdatedAt NullTime `xml:"updated_at,omitempty"`

// The following are only valid with an `Avalara for Communications` integration
AvalaraTransactionType int `xml:"avalara_transaction_type,omitempty"`
AvalaraServiceType int `xml:"avalara_service_type,omitempty"`

StartDate NullTime `xml:"start_date,omitempty"`
EndDate NullTime `xml:"end_date,omitempty"`
CreatedAt NullTime `xml:"created_at,omitempty"`
UpdatedAt NullTime `xml:"updated_at,omitempty"`
}

// MarshalXML marshals only the fields needed for creating/updating adjustments
// with the recurly API.
func (a Adjustment) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.Encode(struct {
XMLName xml.Name `xml:"adjustment"`
Description string `xml:"description,omitempty"`
AccountingCode string `xml:"accounting_code,omitempty"`
RevenueScheduleType string `xml:"revenue_schedule_type,omitempty"`
ProductCode string `xml:"product_code,omitempty"`
UnitAmountInCents NullInt `xml:"unit_amount_in_cents,omitempty"`
Quantity int `xml:"quantity,omitempty"`
Currency string `xml:"currency,omitempty"`
TaxCode string `xml:"tax_code,omitempty"`
TaxExempt NullBool `xml:"tax_exempt,omitempty"`
StartDate NullTime `xml:"start_date,omitempty"`
EndDate NullTime `xml:"end_date,omitempty"`
XMLName xml.Name `xml:"adjustment"`
Description string `xml:"description,omitempty"`
AccountingCode string `xml:"accounting_code,omitempty"`
RevenueScheduleType string `xml:"revenue_schedule_type,omitempty"`
ProductCode string `xml:"product_code,omitempty"`
Origin string `xml:"origin,omitempty"`
UnitAmountInCents NullInt `xml:"unit_amount_in_cents,omitempty"`
Quantity int `xml:"quantity,omitempty"`
Currency string `xml:"currency,omitempty"`
TaxCode string `xml:"tax_code,omitempty"`
TaxExempt NullBool `xml:"tax_exempt,omitempty"`
AvalaraTransactionType int `xml:"avalara_transaction_type,omitempty"`
AvalaraServiceType int `xml:"avalara_service_type,omitempty"`
StartDate NullTime `xml:"start_date,omitempty"`
EndDate NullTime `xml:"end_date,omitempty"`
}{
Description: a.Description,
AccountingCode: a.AccountingCode,
RevenueScheduleType: a.RevenueScheduleType,
ProductCode: a.ProductCode,
UnitAmountInCents: a.UnitAmountInCents,
Quantity: a.Quantity,
Currency: a.Currency,
TaxCode: a.TaxCode,
TaxExempt: a.TaxExempt,
StartDate: a.StartDate,
EndDate: a.EndDate,
Description: a.Description,
AccountingCode: a.AccountingCode,
RevenueScheduleType: a.RevenueScheduleType,
ProductCode: a.ProductCode,
Origin: a.Origin,
UnitAmountInCents: a.UnitAmountInCents,
Quantity: a.Quantity,
Currency: a.Currency,
TaxCode: a.TaxCode,
TaxExempt: a.TaxExempt,
AvalaraServiceType: a.AvalaraServiceType,
AvalaraTransactionType: a.AvalaraTransactionType,
StartDate: a.StartDate,
EndDate: a.EndDate,
})
}

Expand Down Expand Up @@ -155,6 +165,8 @@ type TaxDetail struct {
Type string `xml:"type,omitempty"`
TaxRate float64 `xml:"tax_rate,omitempty"`
TaxInCents int `xml:"tax_in_cents,omitempty"`
Level string `xml:"level,omitempty"`
Billable NullBool `xml:"billable,omitempty"`
}

var _ AdjustmentsService = &adjustmentsImpl{}
Expand Down
25 changes: 24 additions & 1 deletion adjustments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ func TestAdjustments_Encoding(t *testing.T) {
</adjustment>
`),
},
{
v: recurly.Adjustment{Origin: "external_gift_card", UnitAmountInCents: recurly.NewInt(-2000), Currency: "USD"},
expected: MustCompactString(`
<adjustment>
<origin>external_gift_card</origin>
<unit_amount_in_cents>-2000</unit_amount_in_cents>
<currency>USD</currency>
</adjustment>
`),
},

{
v: recurly.Adjustment{Description: "Charge for extra bandwidth", ProductCode: "bandwidth", UnitAmountInCents: recurly.NewInt(2000), Currency: "USD"},
expected: MustCompactString(`
Expand Down Expand Up @@ -116,6 +127,17 @@ func TestAdjustments_Encoding(t *testing.T) {
</adjustment>
`),
},
{
v: recurly.Adjustment{UnitAmountInCents: recurly.NewInt(2000), Currency: "USD", AvalaraServiceType: 600, AvalaraTransactionType: 3},
expected: MustCompactString(`
<adjustment>
<unit_amount_in_cents>2000</unit_amount_in_cents>
<currency>USD</currency>
<avalara_transaction_type>3</avalara_transaction_type>
<avalara_service_type>600</avalara_service_type>
</adjustment>
`),
},
}

for i, tt := range tests {
Expand Down Expand Up @@ -279,7 +301,6 @@ func NewTestAdjustment() *recurly.Adjustment {
TaxInCents: 175,
TotalInCents: 2175,
Currency: "USD",
Taxable: recurly.NewBool(false),
TaxType: "usst",
TaxRegion: "CA",
TaxRate: 0.0875,
Expand All @@ -291,6 +312,8 @@ func NewTestAdjustment() *recurly.Adjustment {
Type: "state",
TaxRate: 0.065,
TaxInCents: 130,
Billable: recurly.NewBool(true),
Level: "state",
},
{
XMLName: xml.Name{Local: "tax_detail"},
Expand Down
79 changes: 79 additions & 0 deletions automated_exports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package recurly

import (
"context"
"encoding/xml"
"fmt"
"net/http"
"time"
)

// AutomatedExportsService manages the interactions for automated exports.
type AutomatedExportsService interface {
// Get retrieves export file.
//
// https://dev.recurly.com/docs/download-export-file
Get(ctx context.Context, date time.Time, fileName string) (*AutomatedExport, error)

// ListDates returns a list of dates with export files.
//
// https://dev.recurly.com/v2.8/docs/list-export-dates
ListDates(opts *PagerOptions) Pager

// ListFiles returns a list of files available for the date specified.
//
// https://dev.recurly.com/v2.8/docs/list-export-files
ListFiles(date time.Time, opts *PagerOptions) Pager
}

// AutomatedExport holds export file info.
type AutomatedExport struct {
XMLName xml.Name `xml:"export_file"`
ExpiresAt NullTime `xml:"expires_at,omitempty"`
DownloadURL string `xml:"download_url,omitempty"`
}

// ExportDate holds export date info.
type ExportDate struct {
XMLName xml.Name `xml:"export_date"`
Date string `xml:"date,omitempty"`
}

// ExportFile holds export file info.
type ExportFile struct {
XMLName xml.Name `xml:"export_file"`
Name string `xml:"name,omitempty"`
}

var _ AutomatedExportsService = &automatedExportsImpl{}

// automatedExportsImpl implements AutomatedExportsService.
type automatedExportsImpl serviceImpl

func (s *automatedExportsImpl) Get(ctx context.Context, date time.Time, fileName string) (*AutomatedExport, error) {
d := date.Format("2006-01-02")
path := fmt.Sprintf("/export_dates/%s/export_files/%s", d, fileName)
req, err := s.client.newRequest("GET", path, nil)
if err != nil {
return nil, err
}

var dst AutomatedExport
if _, err := s.client.do(ctx, req, &dst); err != nil {
if e, ok := err.(*ClientError); ok && e.Response.StatusCode == http.StatusNotFound {
return nil, nil
}
return nil, err
}
return &dst, nil
}

func (s *automatedExportsImpl) ListDates(opts *PagerOptions) Pager {
return s.client.newPager("GET", "/export_dates", opts)
}

func (s *automatedExportsImpl) ListFiles(date time.Time, opts *PagerOptions) Pager {
d := date.Format("2006-01-02")
path := fmt.Sprintf("/export_dates/%s/export_files", d)
return s.client.newPager("GET", path, opts)
}
Loading

0 comments on commit 0c3af72

Please sign in to comment.