sessions parsing
This commit is contained in:
parent
27864aa9fc
commit
a43f0d309e
11
WORKLOG.md
11
WORKLOG.md
@ -29,17 +29,6 @@
|
||||
|
||||
*$$$*
|
||||
|
||||
clicks buy
|
||||
creates stripe order / fill 0x order
|
||||
|
||||
server notified of payment
|
||||
txs <- payment
|
||||
|
||||
|
||||
buy supporter pack
|
||||
account credited with features
|
||||
char sets
|
||||
emotes
|
||||
|
||||
|
||||
* balances table (ingame currency)
|
||||
|
||||
@ -465,6 +465,7 @@ header {
|
||||
.stripe-btn {
|
||||
width: 100%;
|
||||
padding: 0 0.5em;
|
||||
margin: 0.25em 0;
|
||||
background: whitesmoke;
|
||||
color: black;
|
||||
border-radius: 2px;
|
||||
|
||||
@ -15,7 +15,7 @@ function BitsBtn(args) {
|
||||
stripe,
|
||||
account,
|
||||
} = args;
|
||||
function onClick(e) {
|
||||
function subscribeClick(e) {
|
||||
stripe.redirectToCheckout({
|
||||
items: [{plan: 'plan_FGmRwawcOJJ7Nv', quantity: 1}],
|
||||
successUrl: 'http://localhost:40080/payments/success',
|
||||
@ -23,16 +23,31 @@ function BitsBtn(args) {
|
||||
clientReferenceId: account.id
|
||||
});
|
||||
}
|
||||
|
||||
function bitsClick(e) {
|
||||
stripe.redirectToCheckout({
|
||||
items: [{sku: 'sku_FHUfNEhWQaVDaT', quantity: 1}],
|
||||
successUrl: 'http://localhost:40080/payments/success',
|
||||
cancelUrl: 'http://localhost:40080/payments/cancel',
|
||||
clientReferenceId: account.id
|
||||
});
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div id="error-message"></div>
|
||||
<button
|
||||
onClick={onClick}
|
||||
onClick={subscribeClick}
|
||||
class="stripe-btn"
|
||||
id="checkout-button-sku_FGjPl1YKoT241P"
|
||||
role="link">
|
||||
Subscribe
|
||||
</button>
|
||||
<button
|
||||
onClick={bitsClick}
|
||||
class="stripe-btn"
|
||||
role="link">
|
||||
Get Bits
|
||||
</button>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ uuid = { version = "0.5", features = ["serde", "v4"] }
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_cbor = "0.9"
|
||||
serde_json = "1.0"
|
||||
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
@ -28,7 +29,9 @@ fern = "0.5"
|
||||
actix = "0.8.2"
|
||||
actix-web = "1.0.0"
|
||||
actix-web-actors = "1.0.0"
|
||||
futures = "0.1"
|
||||
bytes = "0.4"
|
||||
actix-cors = "0.1.0"
|
||||
|
||||
stripe-rust = "0.10.2"
|
||||
stripe-rust = { version = "0.10", features = ["webhooks"] }
|
||||
|
||||
[patch.crates-io]
|
||||
stripe-rust = { path = "/home/ntr/code/stripe-rs" }
|
||||
|
||||
@ -10,6 +10,7 @@ extern crate r2d2_postgres;
|
||||
extern crate fallible_iterator;
|
||||
|
||||
extern crate actix;
|
||||
extern crate actix_cors;
|
||||
extern crate actix_web;
|
||||
extern crate actix_web_actors;
|
||||
|
||||
@ -58,7 +59,7 @@ fn setup_logger() -> Result<(), fern::InitError> {
|
||||
))
|
||||
})
|
||||
.level_for("postgres", log::LevelFilter::Info)
|
||||
.level(log::LevelFilter::Info)
|
||||
.level(log::LevelFilter::Debug)
|
||||
.chain(std::io::stdout())
|
||||
.chain(fern::log_file("log/mnml.log")?)
|
||||
.apply()?;
|
||||
|
||||
@ -1,15 +1,115 @@
|
||||
use uuid::Uuid;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use stripe::{CheckoutSession};
|
||||
use actix::prelude::*;
|
||||
|
||||
use net::{State, MnmlError};
|
||||
use stripe::{Event, EventObject, CheckoutSession};
|
||||
|
||||
pub fn stripe_payment(state: web::Data<State>, body: web::Json::<CheckoutSession>) -> Result<HttpResponse, MnmlError> {
|
||||
let db = state.pool.get().or(Err(MnmlError::ServerError))?;
|
||||
let mut tx = db.transaction().or(Err(MnmlError::ServerError))?;
|
||||
use net::{State, PgPool, MnmlError};
|
||||
|
||||
let session = body.into_inner();
|
||||
pub struct PaymentProcessor {
|
||||
pool: PgPool,
|
||||
}
|
||||
|
||||
info!("{:?}", session);
|
||||
impl Actor for PaymentProcessor {
|
||||
type Context = Context<Self>;
|
||||
|
||||
fn started(&mut self, _ctx: &mut Self::Context) {
|
||||
info!("listening for PaymentProcessor");
|
||||
}
|
||||
}
|
||||
|
||||
impl Supervised for PaymentProcessor {
|
||||
fn restarting(&mut self, _ctx: &mut Context<PaymentProcessor>) {
|
||||
warn!("PaymentProcessor restarting");
|
||||
}
|
||||
}
|
||||
|
||||
impl PaymentProcessor {
|
||||
pub fn new(pool: PgPool) -> PaymentProcessor {
|
||||
PaymentProcessor { pool }
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler<StripeEvent> for PaymentProcessor {
|
||||
type Result = Result<Vec<Mtx>, MnmlError>;
|
||||
|
||||
fn handle(&mut self, msg: StripeEvent, _: &mut Context<Self>) -> Self::Result {
|
||||
let event = msg.0;
|
||||
match event.data.object {
|
||||
EventObject::CheckoutSession(s) => process_stripe_checkout(s),
|
||||
_ => Err(MnmlError::ServerError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_stripe_checkout(session: CheckoutSession) -> Result<Vec<Mtx>, MnmlError> {
|
||||
let account = match session.client_reference_id {
|
||||
Some(a) => Uuid::parse_str(&a).or(Err(MnmlError::UnknownUser))?,
|
||||
None => {
|
||||
warn!("unknown user checkout {:?}", session);
|
||||
return Err(MnmlError::UnknownUser)
|
||||
},
|
||||
};
|
||||
|
||||
// checkout.session.completed
|
||||
// assign stripe customer_id to account
|
||||
|
||||
// if subscription
|
||||
// go get it
|
||||
// set account sub to active and end date
|
||||
|
||||
// if just bits purchase
|
||||
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug,Clone,Copy,Serialize,Deserialize)]
|
||||
enum Mtx {
|
||||
Subscription,
|
||||
Currency,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||
struct Order {
|
||||
account: Uuid,
|
||||
customer: String,
|
||||
}
|
||||
|
||||
struct StripeEvent(Event);
|
||||
impl Message for StripeEvent {
|
||||
type Result = Result<Vec<Mtx>, MnmlError>;
|
||||
}
|
||||
|
||||
pub fn post_stripe_event(state: web::Data<State>, body: web::Json::<Event>) -> Result<HttpResponse, MnmlError> {
|
||||
let event: Event = body.into_inner();
|
||||
info!("stripe event {:?}", event);
|
||||
state.payments.do_send(StripeEvent(event));
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
|
||||
#[test]
|
||||
fn test_stripe_checkout() {
|
||||
let mut f = File::open("./test/checkout.session.completed.purchase.json").expect("couldn't open file");
|
||||
let mut checkout_str = String::new();
|
||||
f.read_to_string(&mut checkout_str)
|
||||
.expect("unable to read file");
|
||||
|
||||
let event: Event = serde_json::from_str(&checkout_str)
|
||||
.expect("could not deserialize");
|
||||
|
||||
let mtx = match event.data.object {
|
||||
EventObject::CheckoutSession(s) => process_stripe_checkout(s),
|
||||
_ => panic!("unknown event obj"),
|
||||
};
|
||||
|
||||
println!("got some fuckin bling {:?}", mtx);
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
use std::env;
|
||||
|
||||
use actix_web::{middleware, web, App, HttpMessage, HttpRequest, HttpResponse, HttpServer};
|
||||
use actix_web::middleware::cors::Cors;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::{Cookie};
|
||||
use actix_web::cookie::{SameSite};
|
||||
use actix_cors::Cors;
|
||||
|
||||
use actix::prelude::*;
|
||||
|
||||
@ -17,7 +17,7 @@ use warden::{Warden};
|
||||
use pubsub::PubSub;
|
||||
use ws::{connect};
|
||||
use account::{account_login, account_create, account_from_token, account_set_token};
|
||||
use mtx::{stripe_payment};
|
||||
use mtx::{PaymentProcessor, post_stripe_event};
|
||||
|
||||
pub type Db = PooledConnection<PostgresConnectionManager>;
|
||||
pub type PgPool = Pool<PostgresConnectionManager>;
|
||||
@ -32,6 +32,9 @@ pub enum MnmlError {
|
||||
Unauthorized,
|
||||
#[fail(display="bad request")]
|
||||
BadRequest,
|
||||
|
||||
#[fail(display="unknown user")]
|
||||
UnknownUser,
|
||||
}
|
||||
|
||||
impl ResponseError for MnmlError {
|
||||
@ -51,6 +54,9 @@ impl ResponseError for MnmlError {
|
||||
.max_age(-1) // 1 week aligns with db set
|
||||
.finish())
|
||||
.json(RpcErrorResponse { err: "unauthorized ".to_string() }),
|
||||
|
||||
MnmlError::UnknownUser => HttpResponse::BadRequest()
|
||||
.json(RpcErrorResponse { err: "unknown user".to_string() }),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,6 +145,8 @@ fn create_pool(url: String) -> Pool<PostgresConnectionManager> {
|
||||
|
||||
pub struct State {
|
||||
pub pool: PgPool,
|
||||
pub payments: Addr<PaymentProcessor>,
|
||||
pub pubsub: Addr<PubSub>,
|
||||
secure: bool,
|
||||
}
|
||||
|
||||
@ -147,23 +155,28 @@ pub fn start() {
|
||||
.expect("DATABASE_URL must be set");
|
||||
let pool = create_pool(database_url);
|
||||
|
||||
let sys = System::new("mnml");
|
||||
let _sys = System::new("mnml");
|
||||
|
||||
Warden::new(pool.clone()).start();
|
||||
let _warden = Warden::new(pool.clone()).start();
|
||||
|
||||
let payments = PaymentProcessor::new(pool.clone()).start();
|
||||
|
||||
let pubsub_conn = pool.get().expect("could not get pubsub pg connection");
|
||||
let pubsub_addr = Supervisor::start(move |_| PubSub::new(pubsub_conn));
|
||||
let pubsub = Supervisor::start(move |_| PubSub::new(pubsub_conn));
|
||||
|
||||
HttpServer::new(move || App::new()
|
||||
.data(State { pool: pool.clone(), secure: false })
|
||||
.data(State { pool: pool.clone(), secure: false, payments: payments.clone(), pubsub: pubsub.clone() })
|
||||
.wrap(middleware::Logger::default())
|
||||
.wrap(Cors::new().supports_credentials())
|
||||
.service(web::resource("/api/login").route(web::post().to(login)))
|
||||
.service(web::resource("/api/logout").route(web::post().to(logout)))
|
||||
.service(web::resource("/api/register").route(web::post().to(register)))
|
||||
|
||||
.service(web::resource("/api/payments/stripe").route(web::post().to(stripe_payment)))
|
||||
.service(web::resource("/api/payments/crypto").route(web::post().to(stripe_payment)))
|
||||
.service(web::resource("/api/payments/stripe")
|
||||
.route(web::post().to(post_stripe_event)))
|
||||
|
||||
// .service(web::resource("/api/payments/crypto")
|
||||
// .route(web::post().to(post_stripe_payment)))
|
||||
|
||||
.service(web::resource("/api/ws").route(web::get().to(connect))))
|
||||
.bind("127.0.0.1:40000").expect("could not bind to port")
|
||||
|
||||
64
server/test/checkout.session.completed.purchase.json
Normal file
64
server/test/checkout.session.completed.purchase.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"id": "evt_1Emw4mGf8y65MteUNt6J2zlN",
|
||||
"object": "event",
|
||||
"api_version": "2019-05-16",
|
||||
"created": 1560921076,
|
||||
"data": {
|
||||
"object": {
|
||||
"id": "cs_test_0FldEAQpyiya1xFLtZpPuEaWQ8OGnMMsotlnwrWCk8jXrAeeMtVruHwB",
|
||||
"object": "checkout.session",
|
||||
"billing_address_collection": null,
|
||||
"cancel_url": "http://localhost:40080/payments/cancel",
|
||||
"client_reference_id": "ff3bbecb-e744-4674-b411-a11d6832a5ac",
|
||||
"customer": "cus_FHV47hm01bNBpG",
|
||||
"customer_email": null,
|
||||
"display_items": [
|
||||
{
|
||||
"amount": 500,
|
||||
"currency": "aud",
|
||||
"quantity": 1,
|
||||
"sku": {
|
||||
"id": "sku_FHUfNEhWQaVDaT",
|
||||
"object": "sku",
|
||||
"active": true,
|
||||
"attributes": {
|
||||
"name": "20 bits"
|
||||
},
|
||||
"created": 1560919564,
|
||||
"currency": "aud",
|
||||
"image": null,
|
||||
"inventory": {
|
||||
"quantity": null,
|
||||
"type": "infinite",
|
||||
"value": null
|
||||
},
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"package_dimensions": null,
|
||||
"price": 500,
|
||||
"product": "prod_FHUfY9DFwl0pPl",
|
||||
"updated": 1560919796
|
||||
},
|
||||
"type": "sku"
|
||||
}
|
||||
],
|
||||
"livemode": false,
|
||||
"locale": null,
|
||||
"payment_intent": "pi_1Emw4WGf8y65MteUBrVOy4ME",
|
||||
"payment_method_types": [
|
||||
"card"
|
||||
],
|
||||
"submit_type": null,
|
||||
"subscription": null,
|
||||
"success_url": "http://localhost:40080/payments/success"
|
||||
}
|
||||
},
|
||||
"livemode": false,
|
||||
"pending_webhooks": 3,
|
||||
"request": {
|
||||
"id": null,
|
||||
"idempotency_key": null
|
||||
},
|
||||
"type": "checkout.session.completed"
|
||||
}
|
||||
63
server/test/checkout.session.completed.subscription.json
Normal file
63
server/test/checkout.session.completed.subscription.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"id": "evt_1EmH4cGf8y65MteUj9qhzJzD",
|
||||
"object": "event",
|
||||
"api_version": "2019-05-16",
|
||||
"created": 1560763462,
|
||||
"data": {
|
||||
"object": {
|
||||
"id": "cs_test_RH4RVfajVXRAcruFssXLthaDnfVBJGiPumUfMzPdj5DixpieRl645hkQ",
|
||||
"object": "checkout.session",
|
||||
"billing_address_collection": null,
|
||||
"cancel_url": "http://localhost:40080/payments/cancel",
|
||||
"client_reference_id": "ff3bbecb-e744-4674-b411-a11d6832a5ac",
|
||||
"customer": "cus_FGoioD9GGtTXlW",
|
||||
"customer_email": null,
|
||||
"display_items": [
|
||||
{
|
||||
"amount": 1000,
|
||||
"currency": "aud",
|
||||
"plan": {
|
||||
"id": "plan_FGmRwawcOJJ7Nv",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_FGmRFYmB700pM5",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"type": "plan"
|
||||
}
|
||||
],
|
||||
"livemode": false,
|
||||
"locale": null,
|
||||
"payment_intent": null,
|
||||
"payment_method_types": [
|
||||
"card"
|
||||
],
|
||||
"submit_type": null,
|
||||
"subscription": "sub_FGoiRaWHZUF01V",
|
||||
"success_url": "http://localhost:40080/payments/success"
|
||||
}
|
||||
},
|
||||
"livemode": false,
|
||||
"pending_webhooks": 1,
|
||||
"request": {
|
||||
"id": null,
|
||||
"idempotency_key": null
|
||||
},
|
||||
"type": "checkout.session.completed"
|
||||
}
|
||||
43
server/test/checkout.session.completed.test.json
Normal file
43
server/test/checkout.session.completed.test.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"created": 1326853478,
|
||||
"livemode": false,
|
||||
"id": "evt_00000000000000",
|
||||
"type": "checkout.session.completed",
|
||||
"object": "event",
|
||||
"request": null,
|
||||
"pending_webhooks": 1,
|
||||
"api_version": "2019-05-16",
|
||||
"data": {
|
||||
"object": {
|
||||
"id": "cs_00000000000000",
|
||||
"object": "checkout.session",
|
||||
"billing_address_collection": null,
|
||||
"cancel_url": "https://example.com/cancel",
|
||||
"client_reference_id": null,
|
||||
"customer": null,
|
||||
"customer_email": null,
|
||||
"display_items": [
|
||||
{
|
||||
"amount": 1500,
|
||||
"currency": "usd",
|
||||
"custom": {
|
||||
"description": "Comfortable cotton t-shirt",
|
||||
"images": null,
|
||||
"name": "T-shirt"
|
||||
},
|
||||
"quantity": 2,
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"livemode": false,
|
||||
"locale": null,
|
||||
"payment_intent": "pi_00000000000000",
|
||||
"payment_method_types": [
|
||||
"card"
|
||||
],
|
||||
"submit_type": null,
|
||||
"subscription": null,
|
||||
"success_url": "https://example.com/success"
|
||||
}
|
||||
}
|
||||
}
|
||||
109
server/test/customer.subscription.created.json
Normal file
109
server/test/customer.subscription.created.json
Normal file
@ -0,0 +1,109 @@
|
||||
{
|
||||
"created": 1326853478,
|
||||
"livemode": false,
|
||||
"id": "evt_00000000000000",
|
||||
"type": "customer.subscription.created",
|
||||
"object": "event",
|
||||
"request": null,
|
||||
"pending_webhooks": 1,
|
||||
"api_version": "2019-05-16",
|
||||
"data": {
|
||||
"object": {
|
||||
"id": "sub_00000000000000",
|
||||
"object": "subscription",
|
||||
"application_fee_percent": null,
|
||||
"billing": "charge_automatically",
|
||||
"billing_cycle_anchor": 1560755136,
|
||||
"billing_thresholds": null,
|
||||
"cancel_at": null,
|
||||
"cancel_at_period_end": false,
|
||||
"canceled_at": null,
|
||||
"collection_method": "charge_automatically",
|
||||
"created": 1560755136,
|
||||
"current_period_end": 1563347136,
|
||||
"current_period_start": 1560755136,
|
||||
"customer": "cus_00000000000000",
|
||||
"days_until_due": null,
|
||||
"default_payment_method": "pm_1EmEuIGf8y65MteU7aM67eNH",
|
||||
"default_source": null,
|
||||
"default_tax_rates": [
|
||||
],
|
||||
"discount": null,
|
||||
"ended_at": null,
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_00000000000000",
|
||||
"object": "subscription_item",
|
||||
"billing_thresholds": null,
|
||||
"created": 1560755136,
|
||||
"metadata": {
|
||||
},
|
||||
"plan": {
|
||||
"id": "plan_00000000000000",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_00000000000000",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"subscription": "sub_00000000000000"
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_FGmToAWeV09Z35"
|
||||
},
|
||||
"latest_invoice": "in_1EmEuKGf8y65MteUxapoByyd",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"plan": {
|
||||
"id": "plan_00000000000000",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_00000000000000",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"schedule": null,
|
||||
"start": 1560755136,
|
||||
"start_date": 1560755136,
|
||||
"status": "active",
|
||||
"tax_percent": null,
|
||||
"trial_end": null,
|
||||
"trial_start": null
|
||||
}
|
||||
}
|
||||
}
|
||||
134
server/test/customer.subscription.updated.json
Normal file
134
server/test/customer.subscription.updated.json
Normal file
@ -0,0 +1,134 @@
|
||||
{
|
||||
"created": 1326853478,
|
||||
"livemode": false,
|
||||
"id": "evt_00000000000000",
|
||||
"type": "customer.subscription.updated",
|
||||
"object": "event",
|
||||
"request": null,
|
||||
"pending_webhooks": 1,
|
||||
"api_version": "2019-05-16",
|
||||
"data": {
|
||||
"object": {
|
||||
"id": "sub_00000000000000",
|
||||
"object": "subscription",
|
||||
"application_fee_percent": null,
|
||||
"billing": "charge_automatically",
|
||||
"billing_cycle_anchor": 1560755136,
|
||||
"billing_thresholds": null,
|
||||
"cancel_at": null,
|
||||
"cancel_at_period_end": false,
|
||||
"canceled_at": null,
|
||||
"collection_method": "charge_automatically",
|
||||
"created": 1560755136,
|
||||
"current_period_end": 1563347136,
|
||||
"current_period_start": 1560755136,
|
||||
"customer": "cus_00000000000000",
|
||||
"days_until_due": null,
|
||||
"default_payment_method": "pm_1EmEuIGf8y65MteU7aM67eNH",
|
||||
"default_source": null,
|
||||
"default_tax_rates": [
|
||||
],
|
||||
"discount": null,
|
||||
"ended_at": null,
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_00000000000000",
|
||||
"object": "subscription_item",
|
||||
"billing_thresholds": null,
|
||||
"created": 1560755136,
|
||||
"metadata": {
|
||||
},
|
||||
"plan": {
|
||||
"id": "plan_00000000000000",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_00000000000000",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"subscription": "sub_00000000000000"
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_FGmToAWeV09Z35"
|
||||
},
|
||||
"latest_invoice": "in_1EmEuKGf8y65MteUxapoByyd",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"plan": {
|
||||
"id": "plan_00000000000000",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_00000000000000",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"schedule": null,
|
||||
"start": 1560755136,
|
||||
"start_date": 1560755136,
|
||||
"status": "active",
|
||||
"tax_percent": null,
|
||||
"trial_end": null,
|
||||
"trial_start": null
|
||||
},
|
||||
"previous_attributes": {
|
||||
"plan": {
|
||||
"id": "OLD_00000000000000",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_00000000000000",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed",
|
||||
"name": "Old plan"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
{
|
||||
"object": {
|
||||
"id": "sub_FGmgBxTVX2xM7H",
|
||||
"object": "subscription",
|
||||
"application_fee_percent": null,
|
||||
"billing": "charge_automatically",
|
||||
"billing_cycle_anchor": 1560755931,
|
||||
"billing_thresholds": null,
|
||||
"cancel_at": null,
|
||||
"cancel_at_period_end": false,
|
||||
"canceled_at": null,
|
||||
"collection_method": "charge_automatically",
|
||||
"created": 1560755931,
|
||||
"current_period_end": 1563347931,
|
||||
"current_period_start": 1560755931,
|
||||
"customer": "cus_FGmgIHDimD5Tei",
|
||||
"days_until_due": null,
|
||||
"default_payment_method": "pm_1EmF78Gf8y65MteUQBHQU1po",
|
||||
"default_source": null,
|
||||
"default_tax_rates": [
|
||||
],
|
||||
"discount": null,
|
||||
"ended_at": null,
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_FGmg6F7DZAyU2Y",
|
||||
"object": "subscription_item",
|
||||
"billing_thresholds": null,
|
||||
"created": 1560755932,
|
||||
"metadata": {
|
||||
},
|
||||
"plan": {
|
||||
"id": "plan_FGmRwawcOJJ7Nv",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_FGmRFYmB700pM5",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"subscription": "sub_FGmgBxTVX2xM7H",
|
||||
"tax_rates": [
|
||||
]
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_FGmgBxTVX2xM7H"
|
||||
},
|
||||
"latest_invoice": "in_1EmF7AGf8y65MteUvH8MCela",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"plan": {
|
||||
"id": "plan_FGmRwawcOJJ7Nv",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 1000,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1560755040,
|
||||
"currency": "aud",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
},
|
||||
"nickname": "basic",
|
||||
"product": "prod_FGmRFYmB700pM5",
|
||||
"tiers": null,
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"schedule": null,
|
||||
"start": 1560755931,
|
||||
"start_date": 1560755931,
|
||||
"status": "active",
|
||||
"tax_percent": null,
|
||||
"trial_end": null,
|
||||
"trial_start": null
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user