diff --git a/uncloud/uncloud/secrets_sample.py b/uncloud/uncloud/secrets_sample.py
index 36ff0df..464662f 100644
--- a/uncloud/uncloud/secrets_sample.py
+++ b/uncloud/uncloud/secrets_sample.py
@@ -14,4 +14,7 @@ LDAP_ADMIN_DN=""
 LDAP_ADMIN_PASSWORD=""
 LDAP_SERVER_URI = ""
 
+# Stripe (Credit Card payments)
+STRIPE_API_key=""
+
 SECRET_KEY="dx$iqt=lc&yrp^!z5$ay^%g5lhx1y3bcu=jg(jx0yj0ogkfqvf"
diff --git a/uncloud/uncloud/settings.py b/uncloud/uncloud/settings.py
index c6c89d5..f28e0f4 100644
--- a/uncloud/uncloud/settings.py
+++ b/uncloud/uncloud/settings.py
@@ -176,3 +176,8 @@ USE_TZ = True
 STATIC_URL = '/static/'
 
 stripe.api_key = uncloud.secrets.STRIPE_KEY
+
+############
+# Stripe
+
+STRIPE_API_KEY = uncloud.secrets.STRIPE_API_KEY
diff --git a/uncloud/uncloud_pay/migrations/0013_paymentmethod_stripe_card_id.py b/uncloud/uncloud_pay/migrations/0013_paymentmethod_stripe_card_id.py
new file mode 100644
index 0000000..df7c065
--- /dev/null
+++ b/uncloud/uncloud_pay/migrations/0013_paymentmethod_stripe_card_id.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.3 on 2020-03-02 20:14
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('uncloud_pay', '0012_orderrecord'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='paymentmethod',
+            name='stripe_card_id',
+            field=models.CharField(blank=True, max_length=32, null=True),
+        ),
+    ]
diff --git a/uncloud/uncloud_pay/models.py b/uncloud/uncloud_pay/models.py
index 9cbeb48..a29dc3c 100644
--- a/uncloud/uncloud_pay/models.py
+++ b/uncloud/uncloud_pay/models.py
@@ -143,10 +143,13 @@ class PaymentMethod(models.Model):
     description = models.TextField()
     primary = models.BooleanField(default=True)
 
+    # Only used for "Stripe" source
+    stripe_card_id = models.CharField(max_length=32, blank=True, null=True)
+
     def charge(self, amount):
         if amount > 0: # Make sure we don't charge negative amount by errors...
             if self.source == 'stripe':
-                # TODO: wire to strip, see meooow-payv1/strip_utils.py
+                # TODO: wire to stripe, see meooow-payv1/strip_utils.py
                 payment = Payment(owner=self.owner, source=self.source, amount=amount)
                 payment.save() # TODO: Check return status
 
diff --git a/uncloud/uncloud_pay/serializers.py b/uncloud/uncloud_pay/serializers.py
index e3ac0eb..6c6c04e 100644
--- a/uncloud/uncloud_pay/serializers.py
+++ b/uncloud/uncloud_pay/serializers.py
@@ -7,6 +7,8 @@ from functools import reduce
 from uncloud_vm.serializers import VMProductSerializer
 from uncloud_vm.models import VMProduct
 
+import uncloud_pay.stripe as stripe
+
 # TODO: remove magic numbers for decimal fields
 class BillRecordSerializer(serializers.Serializer):
     order = serializers.CharField()
@@ -27,10 +29,35 @@ class PaymentSerializer(serializers.ModelSerializer):
         model = Payment
         fields = ['owner', 'amount', 'source', 'timestamp']
 
+class CreditCardSerializer(serializers.Serializer):
+    number = serializers.IntegerField()
+    exp_month = serializers.IntegerField()
+    exp_year = serializers.IntegerField()
+    cvc = serializers.IntegerField()
+
 class PaymentMethodSerializer(serializers.ModelSerializer):
     class Meta:
         model = PaymentMethod
-        fields = '__all__'
+        fields = ['source', 'description', 'primary']
+
+class CreatePaymentMethodSerializer(serializers.ModelSerializer):
+    credit_card = CreditCardSerializer()
+
+    class Meta:
+        model = PaymentMethod
+        fields = ['source', 'description', 'primary', 'credit_card']
+
+    def create(self, validated_data):
+        credit_card = stripe.CreditCard(**validated_data.pop('credit_card'))
+        user = self.context['request'].user
+        customer = stripe.create_customer(user.username, user.email)
+        # TODO check customer error
+        customer_id = customer['response_object']['id']
+        stripe_card = stripe.create_card(customer_id, credit_card)
+        # TODO: check credit card error
+        validated_data['stripe_card_id'] = stripe_card['response_object']['id']
+        payment_method = PaymentMethod.objects.create(**validated_data)
+        return payment_method
 
 class ProductSerializer(serializers.Serializer):
     vms = VMProductSerializer(many=True, read_only=True)
diff --git a/uncloud/uncloud/stripe.py b/uncloud/uncloud_pay/stripe.py
similarity index 55%
rename from uncloud/uncloud/stripe.py
rename to uncloud/uncloud_pay/stripe.py
index ce35fd9..6399a1a 100644
--- a/uncloud/uncloud/stripe.py
+++ b/uncloud/uncloud_pay/stripe.py
@@ -1,5 +1,16 @@
 import stripe
+import stripe.error
+import logging
 
+from django.conf import settings
+
+# Static stripe configuration used below.
+CURRENCY = 'chf'
+
+# Register stripe (secret) API key from config.
+stripe.api_key = settings.STRIPE_API_KEY
+
+# Helper (decorator) used to catch errors raised by stripe logic.
 def handle_stripe_error(f):
     def handle_problems(*args, **kwargs):
         response = {
@@ -53,3 +64,51 @@ def handle_stripe_error(f):
             return response
 
     return handle_problems
+
+# Convenience CC container, also used for serialization.
+class CreditCard():
+    number = None
+    exp_year = None
+    exp_month = None
+    cvc = None
+
+    def __init__(self, number, exp_month, exp_year, cvc):
+        self.number=number
+        self.exp_year = exp_year
+        self.exp_month = exp_month
+        self.cvc = cvc
+
+# Actual Stripe logic.
+
+@handle_stripe_error
+def create_card(customer_id, credit_card):
+    # Test settings
+    credit_card.number = "5555555555554444"
+
+    return stripe.Customer.create_source(
+            customer_id,
+            card={
+                'number': credit_card.number,
+                'exp_month': credit_card.exp_month,
+                'exp_year': credit_card.exp_year,
+                'cvc': credit_card.cvc
+                })
+
+@handle_stripe_error
+def get_card(customer_id, card_id):
+    return stripe.Card.retrieve_source(customer_id, card_id)
+
+@handle_stripe_error
+def charge_customer(amount, source):
+    return stripe.Charge.create(
+            amount=amount,
+            currenty=CURRENCY,
+            source=source)
+
+@handle_stripe_error
+def create_customer(name, email):
+    return stripe.Customer.create(name=name, email=email)
+
+@handle_stripe_error
+def get_customer(customer_id):
+    return stripe.Customer.retrieve(customer_id)
diff --git a/uncloud/uncloud_pay/views.py b/uncloud/uncloud_pay/views.py
index 9ed57c8..aaee9de 100644
--- a/uncloud/uncloud_pay/views.py
+++ b/uncloud/uncloud_pay/views.py
@@ -57,9 +57,15 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
         return get_user_model().objects.all()
 
 class PaymentMethodViewSet(viewsets.ModelViewSet):
-    serializer_class = PaymentMethodSerializer
     permission_classes = [permissions.IsAuthenticated]
 
+    def get_serializer_class(self):
+        if self.action == 'create':
+            return CreatePaymentMethodSerializer
+        else:
+            return PaymentMethodSerializer
+
+
     def get_queryset(self):
         return PaymentMethod.objects.filter(owner=self.request.user)