1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#![allow(dead_code)]

extern crate curl;
extern crate rustc_serialize;
extern crate url;

use curl::http;
use curl::http::handle::Method;
use rustc_serialize::base64::{STANDARD, ToBase64};
use rustc_serialize::json::Json;
use std::fmt::Display;
use std::str::from_utf8;
use url::form_urlencoded::serialize;

pub use error::{Error, ErrorKind};

mod error;
mod macros;

pub mod resources {
	mod account;
	mod application_fees;
	mod balance;
	mod bitcoin_receivers;
	mod charges;
	mod coupons;
	mod customers;
	mod events;
	mod file_uploads;
	mod invoice_items;
	mod invoices;
	mod plans;
	mod recipients;
	mod tokens;
	mod transfers;
}

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const API_BASE: &'static str = "https://api.stripe.com";
const BASE_PATH: &'static str = "/v1";
const CONNECT_BASE: &'static str = "https://connect.stripe.com";
const UPLOADS_BASE: &'static str = "https://uploads.stripe.com";

pub static mut api_key: Option<*const str> = None;
pub static mut api_version: Option<*const str> = None;

pub fn set_api_key(val: &str) {
	unsafe { api_key = Some(val) };
}

pub fn set_api_version(val: &str) {
	unsafe { api_key = Some(val) };
}

pub struct Params<'a> {
	pairs: Vec<(String, String)>,
	content_type: &'a str,
}

impl<'a> Params<'a> {
	pub fn new() -> Self {
		// TODO: Handle file uploads.
		Params { pairs: Vec::new(), content_type: "application/x-www-form-urlencoded" }
	}

	pub fn set<T>(&mut self, key: &str, val: T) where T: Display {
		self.pairs.push((key.to_string(), val.to_string()));
	}

	fn to_body(&self) -> String {
		serialize(self.pairs.iter().map(|&(ref n, ref v)| (&**n, &**v)))
	}
}

pub type Response = Result<Json, Error>;

fn request(method: Method, path: &str, params: Option<&Params>) -> Response {
	let body = params.and_then(|b| Some(b.to_body()));
	let mut handle = http::handle();
	let mut req = match method {
		Method::Get => handle.get(path),
		Method::Post => handle.post(path, body.as_ref().map_or("", |b| &b))
						      .content_type("application/x-www-form-urlencoded"),
		Method::Delete => handle.delete(path),
		_ => panic!("unknown method"),
	};

	unsafe {
		let auth = match api_key {
			Some(val) => format!("{}:", &*val).as_bytes().to_base64(STANDARD),
			None => return Err(Error::new(ErrorKind::AuthentificationError,
										  "No API Key provided")),
		};
		req = req.header("Authorization", &format!("Basic {}", auth))
			 	 .header("Accept", "application/json")
			 	 .header("User-Agent", &format!("Stripe{} RustBindings/{}", BASE_PATH, VERSION));
	
		if api_version.is_some() {
			req = req.header("Stripe-Version", &*api_version.unwrap());
		}

		// TODO: What about X-Stripe-Client-User-Agent?
	}

	let resp = try!(req.exec());
	let body = try!(from_utf8(resp.get_body()));
	let result = try!(Json::from_str(body));

	if let Some(val) = result.find("error") {
		return Err(Error::from_json(val, resp.get_code()));
	}

	Ok(result)
}