Skip to content

Commit

Permalink
console: Cover more Stripe API Surface (Plan, Product, etc). #376
Browse files Browse the repository at this point in the history
  • Loading branch information
epost committed May 7, 2020
1 parent 022bd9f commit afa8602
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 13 deletions.
7 changes: 4 additions & 3 deletions console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@
"devDependencies": {
"concurrently": "^5.0.2",
"parcel-bundler": "^1.12.4",
"purescript": "^0.13.5",
"purescript": "^0.13.6",
"purescript-psa": "^0.7.3",
"spago": "^0.13"
"spago": "^0.14"
},
"dependencies": {
"@statebox/stbx-js": "0.0.31",
"@statebox/style": "0.0.6",
"dagre": "^0.8.4"
"dagre": "^0.8.4",
"firebaseui": "^4.5.0"
}
}
148 changes: 143 additions & 5 deletions console/src/Statebox/Console.purs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Effect.Aff.Class (class MonadAff)
import Effect.Console (log)
import Halogen as H
import Halogen (ComponentHTML)
import Halogen.HTML (HTML, p, text, br, div, ul, li, h2, h3, table, tr, th, td)
import Halogen.HTML (HTML, p, text, br, span, div, ul, li, h2, h3, table, tr, th, td)
import Halogen.Query.HalogenM (HalogenM)

import Statebox.Console.DAO as DAO
Expand All @@ -25,6 +25,8 @@ import Debug.Trace (spy)
type State =
{ customer :: Maybe Stripe.Customer
, paymentMethods :: Array Stripe.PaymentMethod
, subscriptions :: Array Stripe.Subscription
, plans :: Array Stripe.PlanWithExpandedProduct
, accounts :: Array { invoices :: Array Stripe.Invoice
}
, status :: AppStatus
Expand Down Expand Up @@ -87,6 +89,20 @@ handleAction = case _ of
(\x -> H.modify_ $ _ { accounts = [ { invoices: x.data } ] }))
spyM "invoicesEE" $ invoicesEE

-- fetch subscriptions for this customer
subscriptionsEE <- H.liftAff $ DAO.listSubscriptions
subscriptionsEE # either (\e -> H.modify_ $ _ { status = ErrorStatus "Failed to fetch subscriptions." })
(either (\e -> H.modify_ $ _ { status = ErrorStatus "Decoding subscriptions failed."})
(\x -> H.modify_ $ _ { subscriptions = x.data }))
spyM "subscriptionsEE" $ subscriptionsEE

-- fetch plans for this customer
plansEE <- H.liftAff $ DAO.listPlans
plansEE # either (\e -> H.modify_ $ _ { status = ErrorStatus "Failed to fetch plans." })
(either (\e -> H.modify_ $ _ { status = ErrorStatus "Decoding plans failed."})
(\x -> H.modify_ $ _ { plans = x.data }))
spyM "plansEE" $ plansEE

-- fetch the payment methods for this customer
paymentMethodsEE <- H.liftAff $ DAO.listPaymentMethods
paymentMethodsEE # either (\e -> H.modify_ $ _ { status = ErrorStatus "Failed to fetch payment methods." })
Expand All @@ -106,6 +122,10 @@ render state =
, div [] (maybe [] (pure <<< customerHtml) state.customer)
, h3 [] [ text "Customer's payment methods" ]
, div [] (state.paymentMethods <#> paymentMethodHtml)
, h2 [] [ text "Subscriptions" ]
, div [] (state.subscriptions <#> subscriptionHtml)
, h2 [] [ text "Plans" ]
, div [] (state.plans <#> planWithExpandedProductHtml)
, h2 [] [ text "Invoices" ]
, div [] (state.accounts <#> \account -> invoiceSummaries account.invoices)
]
Expand All @@ -118,8 +138,7 @@ invoiceSummaries invoices =
invoiceSummaryLineHtml i =
tr [] [ td [] [ text $ i.customer_email ]
, td [] [ text $ i.account_name ]
, td [] [ text $ i.currency ]
, td [] [ text $ show i.amount_due ]
, td [] [ text $ formatCurrency i.currency i.amount_due ]
]

customerHtml :: m. MonadAff m => Stripe.Customer -> ComponentHTML Action ChildSlots m
Expand All @@ -140,7 +159,7 @@ customerHtml c =
] <>
foldMap addressRowsHtml c.address <>
[ tr [] [ th [] [ text "balance" ]
, td [] [ text $ c.currency <> " " <> show c.balance <> " cents" ]
, td [] [ text $ formatCurrency c.currency c.balance ]
]
, tr [] [ th [] [ text "tax ids" ]
, td [] [ taxIdsHtml c.tax_ids ]
Expand Down Expand Up @@ -171,7 +190,7 @@ paymentMethodHtml pm =
billingDetailsHtml :: m. MonadAff m => Stripe.BillingDetails -> ComponentHTML Action ChildSlots m
billingDetailsHtml bd = nameAddressPhoneHtml bd

nameAddressPhoneHtml :: r m. MonadAff m => { | Stripe.NameAddressPhoneRow () } -> ComponentHTML Action ChildSlots m
nameAddressPhoneHtml :: m. MonadAff m => { | Stripe.NameAddressPhoneRow () } -> ComponentHTML Action ChildSlots m
nameAddressPhoneHtml x =
table [] $
[ tr [] [ th [] [ text "name" ]
Expand Down Expand Up @@ -222,6 +241,125 @@ cardHtml c =
formatExpiryDate :: Stripe.Card -> String
formatExpiryDate card = show c.exp_month <> "/" <> show c.exp_year

formatCurrency :: Stripe.Currency -> Stripe.Amount -> String
formatCurrency currency amount =
show amount <> " " <> currency <> " cents"

timestampHtml :: m. MonadAff m => Stripe.Timestamp -> ComponentHTML Action ChildSlots m
timestampHtml ts = text $ show ts

timestampRangeHtml :: m. MonadAff m => Stripe.Timestamp -> Stripe.Timestamp -> ComponentHTML Action ChildSlots m
timestampRangeHtml start end =
span [] [ timestampHtml start, text " thru ", timestampHtml end ]

subscriptionHtml :: m. MonadAff m => Stripe.Subscription -> ComponentHTML Action ChildSlots m
subscriptionHtml s =
table []
[ tr [] [ td [] [ text "id" ]
, td [] [ text s.id ]
]
, tr [] [ td [] [ text "status" ]
, td [] [ text s.status ]
]
, tr [] [ td [] [ text "quantity" ]
, td [] [ text $ show s.quantity ]
]
, tr [] [ td [] [ text "start date" ]
, td [] [ timestampHtml s.start_date ]
]
, tr [] [ td [] [ text "current period" ]
, td [] [ timestampRangeHtml s.current_period_start s.current_period_end ]
]
, tr [] [ td [] [ text "trial period" ]
, td [] [ timestampRangeHtml s.trial_start s.trial_end ]
]
, tr [] [ td [] [ text "collection method" ]
, td [] [ text s.collection_method ]
]
, tr [] [ td [] [ text "live mode" ]
, td [] [ text $ show s.livemode ]
]
, tr [] [ td [] [ text "items" ]
, td [] (s.items.data <#> subscriptionItemHtml)
]
]

subscriptionItemHtml :: m. MonadAff m => Stripe.SubscriptionItem -> ComponentHTML Action ChildSlots m
subscriptionItemHtml item =
table []
[ tr [] [ td [] [ text "plan" ]
, td [] [ planHtml item.plan ]
]
, tr [] [ td [] [ text "created" ]
, td [] [ text $ show item.created ]
]
]

planHtml :: m. MonadAff m => Stripe.Plan -> ComponentHTML Action ChildSlots m
planHtml plan =
table []
[ tr [] [ td [] [ text "nickname" ]
, td [] [ text $ fromMaybe "-" plan.nickname ]
]
, tr [] [ td [] [ text "product id" ]
, td [] [ text plan.product ]
]
, tr [] [ td [] [ text "created on" ]
, td [] [ timestampHtml plan.created ]
]
, tr [] [ td [] [ text "amount" ]
, td [] [ text $ formatCurrency plan.currency plan.amount ]
]
, tr [] [ td [] [ text "billing scheme" ]
, td [] [ text plan.billing_scheme ]
]
, tr [] [ td [] [ text "interval" ]
, td [] [ text $ plan.interval <> " (" <> show plan.interval_count <> "x)" ]
]
]

--------------------------------------------------------------------------------

planWithExpandedProductHtml :: m. MonadAff m => Stripe.PlanWithExpandedProduct -> ComponentHTML Action ChildSlots m
planWithExpandedProductHtml plan =
table []
[ tr [] [ td [] [ text "nickname" ]
, td [] [ text $ fromMaybe "-" plan.nickname ]
]
, tr [] [ td [] [ text "product" ]
, td [] [ productHtml plan.product ]
]
, tr [] [ td [] [ text "created on" ]
, td [] [ timestampHtml plan.created ]
]
, tr [] [ td [] [ text "amount" ]
, td [] [ text $ formatCurrency plan.currency plan.amount ]
]
, tr [] [ td [] [ text "billing scheme" ]
, td [] [ text plan.billing_scheme ]
]
, tr [] [ td [] [ text "interval" ]
, td [] [ text $ plan.interval <> " (" <> show plan.interval_count <> "x)" ]
]
]

productHtml :: m. MonadAff m => Stripe.Product -> ComponentHTML Action ChildSlots m
productHtml product =
table []
[ tr [] [ td [] [ text "product id" ]
, td [] [ text product.id ]
]
, tr [] [ td [] [ text "name" ]
, td [] [ text product.name ]
]
, tr [] [ td [] [ text "description" ]
, td [] [ text $ fromMaybe "-" product.description ]
]
, tr [] [ td [] [ text "unit" ]
, td [] [ text $ fromMaybe "-" product.unit_label ]
]
]

--------------------------------------------------------------------------------

spyM :: m a. Applicative m => String -> a -> m Unit
Expand Down
24 changes: 24 additions & 0 deletions console/src/Statebox/Console/DAO.purs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,27 @@ listPaymentMethods' =
, method = Left GET
, responseFormat = ResponseFormat.json
}

--------------------------------------------------------------------------------

listSubscriptions :: Aff (Affjax.Error \/ String \/ Stripe.ArrayWrapper Stripe.Subscription)
listSubscriptions = listSubscriptions' # map (map (_.body >>> spy "subscriptions dump" >>> decodeJson))

listSubscriptions' :: Aff (Affjax.Error \/ Response Json)
listSubscriptions' =
Affjax.request $ Affjax.defaultRequest { url = mkUrl "/subscriptions"
, method = Left GET
, responseFormat = ResponseFormat.json
}

--------------------------------------------------------------------------------

listPlans :: Aff (Affjax.Error \/ String \/ Stripe.ArrayWrapper Stripe.PlanWithExpandedProduct)
listPlans = listPlans' # map (map (_.body >>> spy "plans dump" >>> decodeJson))

listPlans' :: Aff (Affjax.Error \/ Response Json)
listPlans' =
Affjax.request $ Affjax.defaultRequest { url = mkUrl "/plans"
, method = Left GET
, responseFormat = ResponseFormat.json
}
2 changes: 2 additions & 0 deletions console/src/Statebox/Console/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ main = runHalogenAff do
initialState :: Console.State
initialState = { customer: Nothing
, paymentMethods: mempty
, subscriptions: mempty
, plans: mempty
, accounts: [ { invoices: mempty } ]
, status: Console.Ok
}
43 changes: 38 additions & 5 deletions console/src/Stripe.purs
Original file line number Diff line number Diff line change
Expand Up @@ -95,33 +95,43 @@ type Subscription =
, customer :: CustomerId
, object :: ObjectTag
, created :: Timestamp
, status :: SubscriptionStatusString
, start_date :: Timestamp
, trial_start :: Timestamp
, trial_end :: Timestamp
, current_period_start :: Timestamp
, current_period_end :: Timestamp
, collection_method :: CollectionMethodString
, latest_invoice :: Maybe InvoiceId
, quantity :: Int
, items :: ArrayWrapper SubscriptionItem
, livemode :: Boolean
}

type SubscriptionId = String

type SubscriptionItem =
{ id :: SubscriptionItemId
, object :: ObjectTag
, quantity :: Int
, subscription :: SubscriptionId
, plan :: Plan
, created :: Timestamp
}

type SubscriptionItemId = String

-- | E.g. `"charge_automatically"`
type CollectionMethod = String
-- | See https://stripe.com/docs/billing/subscriptions/overview#subscription-states.
-- | One of `"trialing"` | `"active"` | `"incomplete"` | `"incomplete_expired"` | `"past_due"` | `"canceled"` | `"unpaid.
type SubscriptionStatusString = String

type Plan =
-- | Either `"charge_automatically"` | `"send_invoice"`.
type CollectionMethodString = String

type Plan' product =
{ id :: PlanId
, object :: ObjectTag
, nickname :: Maybe String
, product :: ProductId
, product :: product
, amount :: Amount
, amount_decimal :: AmountDecimal
, currency :: Currency
Expand All @@ -131,6 +141,10 @@ type Plan =
, interval_count :: Int
}

type Plan = Plan' ProductId

type PlanWithExpandedProduct = Plan' Product

type PlanId = String

-- | E.g. `"per_unit"`
Expand All @@ -139,8 +153,27 @@ type BillingScheme = String
-- | E.g. `"month"`
type Interval = String

--------------------------------------------------------------------------------

-- | https://stripe.com/docs/api/products/object
type Product =
{ id :: ProductId
, name :: String
, description :: Maybe String
, unit_label :: Maybe String
, statement_descriptor :: Maybe String -- ^ will appear on a customer's credit card statement
, created :: Timestamp
, updated :: Timestamp
, images :: Array URL
, active :: Boolean
, livemode :: Boolean
}

type ProductId = String

-- | One of `"good"` | `"service"`.
type ProductTypeString = String

--------------------------------------------------------------------------------

type TaxIdData =
Expand Down

0 comments on commit afa8602

Please sign in to comment.